前言原型模式(Prototype Pattern)是一种创建型设计模式,它允许通过复制(克隆)一个现有对象来创建新对象,而不是通过构造函数new的方式。当直接创建对象的代价较大(如初始化需要大量计算、数据库查询、网络请求),或者对象的类型在运行时才能确定时,原型模式提供了一种高效且灵活的解决方案。在 C++ 中,原型模式通常通过提供一个克隆接口(clone()虚函数)来实现,派生类实现深拷贝自身。原型模式还可以结合原型管理器,集中管理多个可克隆的原型对象。一、原型模式的核心思想1.1 为什么要用原型模式?避免昂贵的对象初始化:如果对象的创建过程非常耗时(比如解析大型配置文件、建立数据库连接池),复制一个已有的原型对象比重新构造要快得多。隐藏对象创建的复杂性:客户端不需要知道对象的具体类型和创建细节,只需调用clone()即可。运行时动态指定对象类型:通过原型管理器,可以在运行时根据配置或条件加载不同的原型,实现类似“工厂”的效果但不增加类爆炸。简化复杂对象的结构:对于拥有大量成员、存在循环引用等复杂结构的对象,复制比重建更可靠。1.2 原型模式的结构┌─────────────────┐ │ Prototype │ (抽象原型) │ + clone() │ └────────▲────────┘ │ ┌───────────┴───────────┐ │ │ ┌──────┴──────┐ ┌──────┴──────┐ │ ConcretePrototypeA │ │ ConcretePrototypeB │ │ + clone() │ │ + clone() │ └────────────────────┘ └────────────────────┘ ▲ ▲ └───────────┬───────────┘ │ ┌─────────┴─────────┐ │ Client │ │ 使用 clone() 复制 │ └───────────────────┘Prototype:抽象基类,声明一个虚clone()方法。ConcretePrototype:具体原型类,实现clone()方法,返回自身的一个深拷贝。Client:客户端通过调用原型的clone()方法获得新对象,而不直接依赖具体类。二、原型模式的分类根据克隆的深度,原型模式分为两类:浅拷贝原型和深拷贝原型。在 C++ 中,这取决于clone()的实现方式。2.1 浅拷贝原型复制方式:仅复制对象的所有非静态成员变量,包括指针成员的值(即地址),而不复制指针指向的数据。适用场景:对象包含的成员是指向共享资源(如只读数据、全局配置)的指针,或者成员没有动态分配资源。风险:多个对象共享同一块动态内存,析构时可能出现 double free 或悬空指针。classShallowPrototype{public:int*data;ShallowPrototype(intval):data(newint(val)){}ShallowPrototype(constShallowPrototype)=default;// 浅拷贝ShallowPrototype*clone(){returnnewShallowPrototype(*this);}};2.2 深拷贝原型复制方式:不仅复制对象本身,还递归复制所有动态分配的资源(如指针指向的内存、打开的文件句柄、网络连接等)。适用场景:对象拥有独占的资源,需要完全独立的新副本。推荐做法:使用原型模式的标准形式,clone()返回一个全新的独立深拷贝对象。本教程后续的示例主要采用深拷贝原型,因为它更安全、更符合“克隆”的直觉。三、应用场景场景类别具体描述对象初始化成本高如从数据库加载的大型数据对象、复杂的数学矩阵、图形图像数据。运行时类型确定系统在运行时根据用户选择或配置决定创建哪种对象,且不希望编写条件分支创建代码。避免重复的复杂配置例如一个文档对象,包含复杂的样式、格式、元数据,通过复制原型直接获得预配置的副本。需要保护现有对象状态复制一个当前状态的对象作为快照,用于撤销(Undo)或备份。系统中类的数量爆炸使用原型管理器代替工厂方法,避免为每个产品创建工厂类。四、C++ 原型模式实现方式4.1 基本实现:克隆接口 + 派生类实现这是最经典的方式,利用虚函数实现多态克隆。#includeiostream#includestring#includememory// 抽象原型classPrototype{public:virtualstd::unique_ptrPrototypeclone()const=0;virtualvoidprint()const=0;virtual~Prototype()=default;};// 具体原型 AclassConcreteA:publicPrototype{private:std::string name_;intvalue_;public:ConcreteA(conststd::stringname,intvalue):name_(name),value_(value){}// 克隆方法:深拷贝std::unique_ptrPrototypeclone()constoverride{returnstd::make_uniqueConcreteA(*this);// 调用拷贝构造函数}voidprint()constoverride{std::cout"ConcreteA:token operator">name_", value="value_std::endl;}// 为了演示修改值的方法voidsetValue(intv){value_=v;}};// 具体原型 BclassConcreteB:publicPrototype{private:doubledata_[3];public:ConcreteB(doublea,doubleb,doublec):data_{a,b,c}{}std::unique_ptrPrototypeclone()constoverride{returnstd::make_uniqueConcreteB(*this);}voidprint()constoverride{std::cout"ConcreteB: data = ["data_[0]", "data_[1]", "