1.1 原型模式
原型模式的设计思想:在软件系统中,创建某一类型的对象,为了简化创建的过程,可以只创建一个对象,然后通过克隆的方式复制出多个相同的对象。
原型模式(Prototype Pattern):是一种对象创建模式,用原型实例指定创建对象的种类,并通过复制这些原型创建新的对象。
1.2 模式结构
(1)(Prototype)抽象原型类
抽象原型类是定义具有克隆自己的方法的接口,是所有具体原型类的公共父类。
(2)ConcretePrototype(具体原型类)
具体原型类实现具体的克隆方法,在克隆方法中返回自己的一个克隆对象。
(3)Client (客户类)
客户类让一个原型克隆自身,从而创建一个新的对象。在客户类中只需要直接实例化或通过工厂方法等创建一个对象,再通过调用该对象的克隆方法复制得到多个相同的对象。
原型模式分类:
原型模式克隆对象时,根据其成员对象是否也在克隆,原型模式可以分为两种形式,深克隆和浅克隆。
(1)浅克隆
浅复制是对值类型的成员变量进行复制, 对引用类型的变量只是对引用进行复制, 实际上两个对象还是指向的同一实例
不定义拷贝构造函数,类会自己生成默认拷贝构造函数,默认拷贝构造函数是浅拷贝
#include <iostream>
using namespace std;
class test {
public:
test(int num) {
test_ptr_ = new int(num);
}
~test() {
if (test_ptr_ != nullptr) {
cout << "delete test" << endl;
delete test_ptr_;
}
test_ptr_ = nullptr;
}
private:
int *test_ptr_;
};
int main()
{
test t1(10);
test t2(t1);
return 0;
}
在一个类中,如果定义有指针成员变量,该指针指向new出来的堆内存。在用一个已经存在的对象初始化一个新对象时调用拷贝构造函数,如果拷贝构造函数中只是将已有对象的指针成员赋值给新对象的指针成员,那么就叫浅拷贝,浅拷贝以后两个对象中的指针成员指向同一个堆内存,在对象析构时,同一块堆内存会释放两次,堆内存释放两次程序会发生core dump,马上崩溃。正确的做法应该是在拷贝构造函数中为新对象开辟一段新内存,将已有对象堆内存里面的数据拷贝到新开辟的内存,这叫做深拷贝,深拷贝以后两个对象中的指针成员指向不同的内存,自己负责释放自己的堆内存。
c++浅拷贝程序崩溃的原因:创建t2的时候程序必然会去调用拷贝构造函数,这时候拷贝构造仅仅只是完成了值拷贝,导致两个指针指向了同一块内存区域。随着程序的运行结束,又去调用析构函数,先是t2去调用析构函数,释放了它指向的内存区域,接着t1又去调用析构函数,这时候析构函数企图释放一块已经被释放的内存区域,程序将会崩溃。
(2)深克隆
深复制不仅值类型的成员变量进行复制, 还对引用类型的成员变量申请存储空间, 让他成为一个新对象
class test {
public:
test(int num) {
test_ptr_ = new int(num);
}
test(const test& right) {
// test_ptr_ = new int(*right.test_ptr_);
test_ptr_ = new int;//为指针分配内存
*test_ptr_ = *right.test_ptr_;//拷贝值
}
~test() {
if (test_ptr_ != nullptr) {
cout << "delete test" << endl;
delete test_ptr_;
}
test_ptr_ = nullptr;
}
private:
int* test_ptr_;
};
int main()
{
test t1(10);
test t2(t1);
return 0;
}
1.3 优缺点
优点:
(1)当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程。
(2)可以动态的减少或增加产品类。
缺点:
(1)需要为每一个类配备一个克隆方法,而且对这个克隆方法需要对类的功能进行通盘考虑。
(2)在实现克隆时,需要编写较为复杂的代码。
1.4 适用场景
(1)创建新对象成本较大(如初始化需要较长的时间),新对象可通过原型模式对已有对象进行复制来获得。
1.5 应用举例
原型模式提供了一个通过已存在对象进行新对象创建的接口(Clone),Clone()实现和具体的实现语言相关,在 C++中我们将通过拷贝构造函数实现之。
#include <iostream>
using namespace std;
/*抽象原型类*/
class Student
{
protected:
int id;
char name[10];
public:
virtual Student* Clone() = 0;
virtual void Show() = 0;
};
/*具体原型类*/
class StudentTypeA :public Student
{
public:
StudentTypeA(const char* name_input)
{
strcpy(name, name_input);
this->id = 0;
cout << "Construction....." << endl;
}
StudentTypeA(const StudentTypeA& other)
{
cout << "Copy Construction..." << endl;
this->id = other.id;
this->id++;
strcpy(this->name, other.name);
}
virtual StudentTypeA* Clone()
{
StudentTypeA* tmp = new StudentTypeA(*this);
return tmp;
}
void Show()
{
cout << "Student id == " << id << " name == " << name << endl;
}
~StudentTypeA()
{
cout << "Deconstruction StudentTypeA" << endl;
}
};
int main()
{
Student *student1 = new StudentTypeA("xiaoming");
Student *student2 = student1->Clone();
Student *student3 = student2->Clone();
student1->Show();
student2->Show();
student3->Show();
return 0;
}
总结:
原型模式通过复制原型(原型)而获得新对象创建的功能,这里原型本身就是"对象工厂"(因为能够生产对象),实际上原型模式和 Builder 模式、AbstractFactory 模式都是通过一个类(对象实例)来专门负责对象的创建工作(工厂对象),它们之间的区别是: Builder 模式重在复杂对象的一步步创建(并不直接返回对象),AbstractFactory 模式重在产生多个相互依赖类的对象,而原型模式重在从自身复制自己创建新类。
参考文献:
【1】原型模式及C++实现:原型模式及C++实现 - 曾经的你| - 博客园
【2】解析设计模式中的Prototype原型模式及在C++中的使用:https://www.jb51.net/article/80696.htm
【3】详解原型模式的浅复制(浅拷贝)和深复制(深拷贝):详解原型模式的浅复制(浅拷贝)和深复制(深拷贝)_志不强者智不达,言不信者行不果-CSDN博客