在开发中,有时候可能对同一种类型要实例化多个对象,类型不改变但是类中的数据发生改变,如果每次都是调用new的话,都会执行一次构造函数,导致效率十分底下,这时候我们考虑原型模式。
原型模式就是用原型实例创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需要知道任何创建的细节。
简单点来说,原型模式就是实例自己去拷贝自己。
下面来看下原型模式的示例代码
#include
#include
class iCloneable {
public:
iCloneable() = default;
~iCloneable() = default;
virtual iCloneable* clone() = 0;
virtual void setResume(int age, const std::string& name) = 0;
virtual void display() = 0;
};
class Resume:public iCloneable {
public:
Resume(int age, const char* name):m_age(age), m_name(name){}
~Resume(){}
Resume(Resume&&) = delete;
Resume& operator=(const Resume&)=delete;
Resume& operator=(Resume&&) = delete;
virtual iCloneable* clone() override{
return new Resume(*this);
}
virtual void setResume(int age, const std::string& name)override {
m_age = age;
m_name = name;
}
virtual void display()override {
printf("age=%d, name=%s\n", m_age, m_name.c_str());
}
private:
Resume(const Resume&) =default;
int m_age;
std::string m_name;
};
int main(int argc, char** argv)
{
Resume* rsm = new Resume(21, "john");
rsm->display();
iCloneable * r1 = rsm->clone();
r1->display();
r1->setResume(22, "Tom");
r1->display();
iCloneable*r2 = r1->clone();
return 0;
}
示例中,iCloneable
是一个基类,提供了三个接口,clone
,setResume
和display
,分别用来克隆自身,初始化数据以及显示数据,子类Resume
继承iCloneable
,重写这三个接口,并隐藏自身的拷贝构造函数,这样,使用者只需要调用clone
接口就可以获得一个自身的拷贝,大大提高了效率。然而当前这种实现方式,如果类成员是动态申请的资源的话,就会出现一些问题,你看出来了吗?
在进行类的拷贝构造时,当前的方式是直接将所有的类成员复制一份,表现出的现象就是上述代码中的r1
与rsm
的age和name在被重新赋值前是相同的,此时,如果类成员是动态资源,会出现什么现象呢,观察下述代码。
#include
#include
class iCloneable {
public:
iCloneable() = default;
~iCloneable() = default;
virtual iCloneable* clone() = 0;
virtual void setResume(int age, const std::string& name) = 0;
virtual void display() = 0;
};
class WorkExperience{
public:
WorkExperience(int year):m_workYear(year){}
~WorkExperience(){}
int getWorkYear(){return m_workYear;}
int m_workYear{5};
};
class Resume:public iCloneable {
public:
Resume(int age, const char* name):m_age(age), m_name(name){
m_workExperience = new WorkExperience(9);
}
~Resume(){}
Resume(Resume&&) = delete;
Resume& operator=(const Resume&)=delete;
Resume& operator=(Resume&&) = delete;
virtual iCloneable* clone() override{
return new Resume(*this);
}
virtual void setResume(int age, const std::string& name)override {
m_age = age;
m_name = name;
}
void setWorkYear(int year){ m_workExperience->m_workYear = year;}
virtual void display()override {
printf("age=%d, name=%s\n", m_age, m_name.c_str());
printf("work year:%d\n", m_workExperience->m_workYear);
}
private:
Resume(const Resume&) =default;
int m_age;
std::string m_name;
WorkExperience* m_workExperience;
};
int main(int argc, char** argv)
{
Resume* rsm = new Resume(21, "john");
rsm->display();
iCloneable * r1 = rsm->clone();
r1->display();
r1->setResume(22, "Tom");
r1->display();
rsm->setWorkYear(17);
r1->display();
iCloneable*r2 = r1->clone();
return 0;
}
输出
age=21, name=john
work year:9
age=21, name=john
work year:9
age=22, name=Tom
work year:9
age=22, name=Tom
work year:17
可以看到,当rsm修改了workYear时,r1的workYear也发生了变化,这就是浅拷贝。
浅拷贝就是拷贝了指针,但是没有拷贝指针指向的内容,导致两个指针指向了相同的一块内容,修改其中任意一个,都会引起另一个也发生变化。
浅拷贝的解决方案就是深拷贝,我们把指针指向的内容也复制一份,就不会出现这个问题了。
#include
#include
class iCloneable {
public:
iCloneable() = default;
~iCloneable() = default;
virtual iCloneable* clone() = 0;
virtual void setResume(int age, const std::string& name) = 0;
virtual void display() = 0;
};
class WorkExperience{
public:
WorkExperience(int year):m_workYear(year){}
~WorkExperience(){}
int getWorkYear(){return m_workYear;}
int m_workYear{5};
};
class Resume:public iCloneable {
public:
Resume(int age, const char* name):m_age(age), m_name(name){
m_workExperience = new WorkExperience(9);
}
~Resume(){}
Resume(Resume&&) = delete;
Resume& operator=(const Resume&)=delete;
Resume& operator=(Resume&&) = delete;
virtual iCloneable* clone() override{
return new Resume(*this);
}
virtual void setResume(int age, const std::string& name)override {
m_age = age;
m_name = name;
}
void setWorkYear(int year){ m_workExperience->m_workYear = year;}
virtual void display()override {
printf("age=%d, name=%s\n", m_age, m_name.c_str());
printf("work year:%d\n", m_workExperience->m_workYear);
}
private:
Resume(const Resume& rsm) {
this->m_age = rsm.m_age;
this->m_name = rsm.m_name;
this->m_workExperience = new WorkExperience(*rsm.m_workExperience);
};
int m_age;
std::string m_name;
WorkExperience* m_workExperience;
};
int main(int argc, char** argv)
{
Resume* rsm = new Resume(21, "john");
rsm->display();
iCloneable * r1 = rsm->clone();
r1->display();
r1->setResume(22, "Tom");
r1->display();
rsm->setWorkYear(17);
r1->display();
iCloneable*r2 = r1->clone();
return 0;
}
控制台输出
age=21, name=john
work year:9
age=21, name=john
work year:9
age=22, name=Tom
work year:9
age=22, name=Tom
work year:9
可以看到,即便是改变了rsm的workYear, r1的workYear还是9。
以上图片来源于程杰的《大话设计模式》
,需要的同学可以关注公众号程序员DeRozan,回复1207领取。
《C++ Primer》
和《Effective C++》
是C++开发者必不可少的书籍,如果你想入门C++,以及想要精进C++开发技术,这两本书可以说必须要有。此外,《Linux高性能服务器编程》以及《Linux多线程服务端编程:使用muduo C++网络库》.(陈硕)》
是快速提高你的linux开发能力的秘籍。《大话设计模式》
可以增强我们的模型提取及设计能力,写出更优雅的代码。同时,《操作系统导论》更是开发必读书目,在网上搜索相关资源也要花费一些力气,需要的同学可以关注公众号【程序员DeRozan】,回复【1207】快速免费领取~