C++11 设计模式5. 原型模式
什么是原型模式?
原型模式⼀种创建型设计模式,该模式的核⼼思想是基于现有的对象创建新的对象,⽽不是从头开始创建。在原型模式中,通常有⼀个原型对象,它被⽤作创建新对象的模板。新对象通过复制原型对象的属性和状态来创建,⽽⽆需知道具体的创建细节。
Prototype模式说简单点,就是提供了一个clone, 通过已存在对象进行新对象创建。clone()实现和具体的实现语言相关,在C++中我们通过拷贝构造函数实现。
那为啥要写clone的接口来实现这个目的呢?直接使用拷贝构造不香么,知乎中看到陈硕大佬对此的一个回答,觉得豁然开朗。
Prototype 的意义在于,你拿到一个 基类指针 Base* , 它指向某个 派生类 Derived 对象, 你想克隆出 Derived对象,但代码中不写出 Derived 的具体类型, 因为有很多派生类,这种情况下你用构造函数是搞不定的。 switch-case 是 bad smells 。 另外,这里考虑 virtual 的性能损失是主次不分, 构造对象需要分配内存,这开销比一次虚函数调用大多了。 –陈硕在知乎中的回答
复习拷贝构造函数实现
在学习原型模式之前,我们先来看一下C++中深拷贝和先拷贝问题
UML
两种角色:
1. prototype(抽象原型类) Monster类
2. ConcretePrototype 具体原型类:在clone方法中 return一个自己的对象
M_undead,M_Element,M_Mechanic
那么什么时候才能使用这个原型对象呢?
我们以 魔兽争霸 这款游戏中的 分身系英雄为例,当 剑圣 出镜像的时候,5狗齐飞,分身斧,这些道具或者技能使用的时候,分裂出来的这些英雄都应该有当前英雄的这些属性,然后将属性微调就可以,例如,分身的攻击力只有原先的30%,受到伤害是原先的150%。就可以用 原型模型。
这里的核心是 魔兽争霸的英雄 属性太多了,还有6个框框的物品栏,分裂的瞬间,都要将这些属性拷贝过去。--也就是说:
如果对象的内部数据比较复杂多变,并且在创建对象的时候希望爆出对象的当前状态,那么用原型模式显然比用工厂方法模式更合适
工厂方法模式和原型模式在创建对象时的异同点
a。都不需要程序员知道所创建对象所属的类名
b.工厂方法模式中的createMonster仍旧属于根据类名来生成新对象
c 原型模式clone是根据现有对象来生成新对象,会调用 copy构造方法 。
d 原型模型不像工厂模式,不需要额外的等级结构创建多个工厂类
原型模式的优缺点:
如果创建新对象的背部数据比较复杂且多变,原型模式创建对象的效率可能高的多
需要在clone中完成对象的拷贝,特别是深拷贝和浅拷贝问题,但是一般都调用 copy构造方法
当前copy构造函数中也可以调用 operator= 的重写。
这样也可以使用 "= " 完成拷贝
原型模式和直接 M_undead(m_undead1) 有啥区别?
既然原型模式的 也是调用 copy构造函数,那么直接用 如下的代码不就行了吗?为啥还弄个原型模式?
M_undead m_undead2 = M_undead(m_undead1)
1. C++才有 copy构造,java ,C#并没有,设计模式是独立于变成 语言存在的,因此原型模式的存在是有意义的。
2.你拿到一个 基类指针 Base* ,
它指向某个 派生类 Derived 对象,
你想克隆出 Derived对象,但代码中不写出 Derived 的具体类型,
因为有很多派生类,这种情况下你用构造函数是搞不定的。
switch-case 是 bad smells 。
代码
在代码中还复习了 移动构造函数,和 移动operator=运算符的重写。
// 原型模型 //原型模式⼀种创建型设计模式, //该模式的核⼼思想是基于现有的对象创建新的对象, //⽽不是从头开始创建。在原型模式中,通常有⼀个原型对象, //它被⽤作创建新对象的模板。 //新对象通过复制原型对象的属性和状态来创建,⽽⽆需知道具体的创建细节。 // //Prototype模式说简单点,就是提供了一个clone, //通过已存在对象进行新对象创建。 //clone()实现和具体的实现语言相关, //在C++中我们通过拷贝构造函数实现。 // //那为啥要写clone的接口来实现这个目的呢? //直接使用拷贝构造不香么? // //Prototype 的意义在于,你拿到一个 基类指针 Base* , //它指向某个 派生类 Derived 对象, //你想克隆出 Derived对象,但代码中不写出 Derived 的具体类型, //因为有很多派生类,这种情况下你用构造函数是搞不定的。 //switch - case 是 bad smells 。 //另外,这里考虑 virtual 的性能损失是主次不分, //构造对象需要分配内存,这开销比一次虚函数调用大多了。 // #define _CRT_SECURE_NO_WARNINGS #include #include "stdlib.h" #include "stdio.h" #include "stdint.h" using namespace std; //在学习原型模式之前,需要先复习一下 C++ 的 深拷贝浅拷贝问题。 //涉及到的知识点:拷贝构造函数,重写赋值运算符函数,移动构造函数,重写移动赋值运算符函数 class Teacher { public: Teacher() = default;//=default 表示C++编译器给无参数的构造方法生成函数体 //有参数的构造方法 Teacher(int age,string sname,char* name,char *othername,char **stuname) :_age(age),_sname(sname), _othername(othername),_stuname(stuname){ cout _sname = obj._sname; // // //如果原先的othername不为nullptr,则先要将原先的othername delete掉。实际代码中 // if (this->_othername!= nullptr) { // delete this->_othername; // this->_othername = nullptr; // } // if (this->_stuname != nullptr) { // for (int i = 0; i _stuname[i]; // this->_stuname[i] = nullptr; // } // delete this->_stuname; // this->_stuname = nullptr; // //delete [] this->_stuname; // } // if (obj._othername != nullptr) { // int othernamelen = strlen(obj._othername) + 1; // this->_othername = (char *)malloc(sizeof(char) * othernamelen); // strcpy(this->_othername,obj._othername); // } // if (obj._stuname != nullptr) { // this->_stuname = (char **)malloc(sizeof(char *) * 5); // for (int i = 0; i _stuname[i] = (char *)malloc(sizeof(char *) *stunamelen); // strcpy(this->_stuname[i],obj._stuname[i]); // } // } // } //} //拷贝构造函数 Teacher(const Teacher& obj) { cout _othername); this->_othername = nullptr; } if (this->_stuname != nullptr) { for (int i = 0; i _stuname[i]); this->_stuname[i] = nullptr; } free( this->_stuname); this->_stuname = nullptr; //delete [] this->_stuname; } if (obj._othername != nullptr) { int othernamelen = strlen(obj._othername) + 1; this->_othername = (char *)malloc(sizeof(char) * othernamelen); strcpy(this->_othername, obj._othername); } if (obj._stuname != nullptr) { this->_stuname = (char **)malloc(sizeof(char *) * 5); for (int i = 0; i _stuname[i] = (char *)malloc(sizeof(char *) *stunamelen); strcpy(this->_stuname[i], obj._stuname[i]); } } } return *this; } //移动拷贝函数 Teacher( Teacher &&obj) { cout _othername); this->_othername = nullptr; } if (this->_stuname != nullptr) { for (int i = 0; i _stuname[i]); this->_stuname[i] = nullptr; } free( this->_stuname); this->_stuname = nullptr; //delete [] this->_stuname; } if (obj._othername != nullptr) { int othernamelen = strlen(obj._othername) + 1; this->_othername = (char *)malloc(sizeof(char) * othernamelen); strcpy(this->_othername, obj._othername); } if (obj._stuname != nullptr) { this->_stuname = (char **)malloc(sizeof(char *) * 5); for (int i = 0; i _stuname[i] = (char *)malloc(sizeof(char *) *stunamelen); strcpy(this->_stuname[i], obj._stuname[i]); } } } // if (obj._othername != nullptr) { free(obj._othername); obj._othername = nullptr; } if (obj._stuname!=nullptr) { for (int i = 0; i _othername != nullptr) { free(this->_othername); this->_othername = nullptr; } if (this->_stuname != nullptr) { for (int i = 0; i _stuname[i]); this->_stuname[i] = nullptr; } free(this->_stuname); this->_stuname = nullptr; //delete [] this->_stuname; } } public: int _age = 0 ; string _sname = ""; char _name[128] = {0}; char *_othername = nullptr; char **_stuname = nullptr; }; void printTea(Teacher *tea) { if (tea == NULL) { return; } printf("------ printTea start ------\n"); printf("tea->age = %d,tea->name = %s,tea->othername=%s\n", tea->_age, tea->_name, tea->_othername); for (size_t j = 0; j stuname[%d] = %s, ", j, tea->_stuname[j]); } printf("\n"); printf("------ printTea end ------\n"); } int main() { _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);//程序退出时检测内存泄漏并显示到“输出”窗口 std::cout