使用场景:当需要构建多个相同的类对象时,而且该类对象结构较为复杂,如果每个都重新组织构建会很麻烦。

        其实,就是写一个拷贝构造函数,或者写一个拷贝每个成员变量的clone()方法。

        举例说明:比如一个相亲网站需要定义一个“男人”类,可想而知描述清楚一个男人肯定需要很多属性吧,比如姓名、身高、体重、职业、财产大致情况、联系电话、微信号、QQ号等。如果一个男人同时有多个相亲对象时,每次都重新构建填写属性显然太麻烦,所以类中需要定义一个拷贝的方法。

先写一个简单的类:

#pragma once
#include <QString>
class singleMan
{
public:
	singleMan(QString name,double height,double weight,QString career,QString phoneNum);
	~singleMan();

	QString m_name;//姓名
	double m_height;//身高
	double m_weight;//体重
	QString m_career;//职业
	QString m_phoneNum;//电话

};
#include "singleMan.h"
singleMan::singleMan(QString name, double height, double weight, QString career, QString phoneNum):m_name(name),m_height(height),
	m_weight(weight),m_career(career),m_phoneNum(phoneNum)
{
}

singleMan::~singleMan()
{
}

直接赋值,打印出的内容也完全一样,还需要写什么拷贝构造吗?

singleMan sman("LaoWang", 178, 153, "programmer", "19969961024");
	singleMan sman1 = sman;
	cout << "sman:" << sman.m_name.toStdString() << ";" << sman.m_phoneNum.toStdString() << endl;
	cout << "sman1:" << sman1.m_name.toStdString() << ";" << sman1.m_phoneNum.toStdString() << endl;

c++设计模式二:原型模式-LMLPHP

        这里c++会自动创建一个默认的拷贝构造函数,将成员变量中的值复制一次。但是,当类中有指针类型成员变量时,默认的构造函数仅仅把地址复制一份,两个对象指向的是同一个内存地址,对象销毁时会造成同一个内存被释放两次造成double free,例子如下,添加一个指针类型成员变量-近半年工资流水:

singleman类中添加double* m_monthSalary指针成员变量:

double* m_monthSalary = nullptr;//近半年工资流水

类的析构中释放指针空间:

singleMan::~singleMan()
{
	if (m_monthSalary!=nullptr)
	{
		delete m_monthSalary;
		m_monthSalary = nullptr;
	}
}
    double salary[6] = { 30000,30000,40000,50000,60000 };
	sman.m_monthSalary = salary;
	singleMan sman2 = sman;
	cout << "sman:" << sman.m_monthSalary[0] << sman.m_monthSalary[1] <<                     sman.m_monthSalary[2] << sman.m_monthSalary[3] << endl;
	cout << "sman2:" << sman2.m_monthSalary[0] << sman2.m_monthSalary[1] << sman2.m_monthSalary[2] << sman2.m_monthSalary[3] << endl;

c++设计模式二:原型模式-LMLPHP

调用打印没问题,但是程序结束后会报错:

c++设计模式二:原型模式-LMLPHP

         所以,这里需要自己写一个拷贝构造函数,实现中需要重新分配新的内存地址(即所谓的深拷贝):

//拷贝构造
	singleMan(const singleMan& man);//这里要用引用,引用做参数时可减少一次对象拷贝,同时避免了循环拷贝构造问题;
singleMan::singleMan(const singleMan& man)
{
	m_career = man.m_career;
	m_height = man.m_height;
	m_name = man.m_name;
	m_phoneNum = man.m_phoneNum;
	m_weight = man.m_weight;
	m_monthSalary = new double[6];
	memcpy(m_monthSalary, man.m_monthSalary, 6 * sizeof(double*));
}
    double* salary = new double[6];
	salary[0] = 30000;
	salary[1] = 30000;
	salary[2] = 40000;
	salary[3] = 50000;
	salary[4] = 60000;
	salary[5] = 65000;
	sman.m_monthSalary = salary;
	singleMan sman2 = sman;
	cout << "sman  salary: " << sman.m_monthSalary << endl;
	cout << "sman2 salary: " << sman2.m_monthSalary << endl;
	cout << "sman: " << sman.m_monthSalary[0]<<"," << sman.m_monthSalary[1] << "," << sman.m_monthSalary[2] << "," << sman.m_monthSalary[3] << endl;
	cout << "sman2:" << sman2.m_monthSalary[0] << "," << sman2.m_monthSalary[1] << "," << sman2.m_monthSalary[2] << "," << sman2.m_monthSalary[3] << endl;

c++设计模式二:原型模式-LMLPHP

        此时,上述问题貌似已经解决了,但是在实际使用过程中会有这种用法:将一个类对象赋值给另一个已经构造好的对象:

    singleMan sman3("LaoLi", 183, 160, "programmer", "19969960512");
	sman3 = sman;
	cout << "sman: " << sman.m_name.toStdString() << ";" << sman.m_phoneNum.toStdString() << endl;
	cout << "sman3:" << sman3.m_name.toStdString() << ";" << sman3.m_phoneNum.toStdString() << endl;
	cout << "sman  salary: " << sman.m_monthSalary << endl;
	cout << "sman3 salary: " << sman3.m_monthSalary << endl;
	cout << "sman: " << sman.m_monthSalary[0] << "," << sman.m_monthSalary[1] << "," << sman.m_monthSalary[2] << "," << sman.m_monthSalary[3] << endl;
	cout << "sman3:" << sman3.m_monthSalary[0] << "," << sman3.m_monthSalary[1] << "," << sman3.m_monthSalary[2] << "," << sman3.m_monthSalary[3] << endl; 

c++设计模式二:原型模式-LMLPHP

        这时,就不进入拷贝构造函数了,而是进入了默认的赋值操作符函数中,会有同样的问题,这时需要写一个赋值操作符重载函数:

    //赋值运算符重载(用新的对象的值全部替换原有项)
	singleMan& operator=(const singleMan& man);
singleMan& singleMan::operator=(const singleMan& man)
{
	if (m_monthSalary != nullptr)
	{
		delete m_monthSalary;
		m_monthSalary = nullptr;
	}
	m_career = man.m_career;
	m_height = man.m_height;
	m_name = man.m_name;
	m_phoneNum = man.m_phoneNum;
	m_weight = man.m_weight;
	m_monthSalary = new double[6];
	memcpy(m_monthSalary, man.m_monthSalary, 6 * sizeof(double*));
	return *this;
}

再次调用问题得到解决:

c++设计模式二:原型模式-LMLPHP

最后,由于拷贝构造函数中代码和赋值运算符代码几乎一致,可以在拷贝构造函数中直接调研赋值运算符重载函数:

singleMan::singleMan(const singleMan& man)
{
	/*m_career = man.m_career;
	m_height = man.m_height;
	m_name = man.m_name;
	m_phoneNum = man.m_phoneNum;
	m_weight = man.m_weight;
	m_monthSalary = new double[6];
	memcpy(m_monthSalary, man.m_monthSalary, 6 * sizeof(double*));*/
	*this = man;
}

        如有问题,欢迎指正! 

参考文献:

【精选】C++创建型模式-原型模式_c++ 原型模式-CSDN博客

4. 原型模式(Prototype) (yuque.com)

10-29 14:42