设计模式之 原型(Prototype)模式(使用智能指针避免浅拷贝时内存泄露)

上一篇文章指出了原型模式中浅拷贝时出现的一些问题,如内存泄露等问题。


下面给出几种可能的解决方案来避免内存泄露。

1. 我们让工作简历类继承克隆接口 实现深度拷贝。

但这时候会出现内存泄露的问题。(拷贝对象内的工作简历指针无法析构)


代码如下:

#include <iostream>
#include <string>
#include <memory>

using namespace std;

template<class T>
class ICloneable
{
public:
	virtual T* clone() = 0;
};

// 工作经历类继承自ICloneable接口 实现深度拷贝
class CWorkExperience :public ICloneable<CWorkExperience>
{
public:
	// 默认构造函数
	CWorkExperience(){}

	CWorkExperience(const string& company, const string& workTime)
	{
		m_strCompany = company;
		m_strWorkTime = workTime;
	}

	// 拷贝构造函数
	CWorkExperience(const CWorkExperience& right)
	{
		m_strCompany = right.m_strCompany;
		m_strWorkTime = right.m_strWorkTime;
	}

	~CWorkExperience()
	{
		cout << "CWorkExperience析构" << endl;
		printInfo();
	}

	CWorkExperience* clone()
	{
		// 深拷贝
		CWorkExperience* pClone = new CWorkExperience(*this);
		return pClone;
	}

	void setCompany(const string& company)
	{
		m_strCompany = company;
	}

	const string& getCompany() const
	{
		return m_strCompany;
	}

	void setWorkTime(const string& workTime)
	{
		m_strWorkTime = workTime;
	}

	const string& getWorkTime() const
	{
		return m_strWorkTime;
	}

	void printInfo()
	{
		cout << "Company: " << m_strCompany << endl;
		cout << "WorkTime: " << m_strWorkTime << endl;
	}

private:
	string m_strCompany;		// company name
	string m_strWorkTime;		// work time
};

// 简历类
class CResume : public ICloneable<CResume>
{
private:
	// 只允许调用带参数的构造函数和拷贝构造函数
	CResume()
	{
		m_experience = NULL;
	}
public:

	// 带参构造函数
	CResume(CWorkExperience* pWorkExperience)
	{
		// 不负责内存分配 只是共享
		m_experience = pWorkExperience;
	}

	// 带参拷贝构造函数
	CResume(const CResume& right)
	{
		m_name = right.m_name;
		m_sex = right.m_sex;
		m_age = right.m_age;

		// 这里实现的是深拷贝
		m_experience = right.m_experience->clone();
	}

	~CResume()
	{
		cout << "CResume析构 " << m_name << endl;
	}

	void setInfo(const string& name, const string& sex
		, int age)
	{
		m_name = name;
		m_sex = sex;
		m_age = age;
	}

	void setExperience(const string& company, const string& workTime)
	{
		m_experience->setCompany(company);
		m_experience->setWorkTime(workTime);
	}

	CResume* clone()
	{
		CResume* resume = new CResume(*this);
		return resume;
	}

	void printInfo()
	{
		cout << "Name: " << m_name << endl;
		cout << "Sex: " << m_sex << endl;
		cout << "Age: " << m_age << endl;
		cout << "Experience: " << endl;
		m_experience->printInfo();
		cout << endl;
	}
protected:
	string m_name;
	string m_sex;
	int m_age;

	CWorkExperience* m_experience;// 指针 聚合
};

void testPrototype()
{
	CWorkExperience* pWorkExperience = new CWorkExperience("MS", "2001.11 - 2005.11");
	CResume re(pWorkExperience);	// 只能这样构造
	re.setInfo("Jacky", "Male", 20);
	re.printInfo();

	CResume* pClone = re.clone();

	pClone->setInfo("Marry", "Female", 30);
	pClone->setExperience("Google", "2006.01 - 2010.01");
	pClone->printInfo();

	re.printInfo();

	delete pClone;
	delete pWorkExperience;
	pClone = NULL;
	pWorkExperience = NULL;
}

int main(void)
{
	testPrototype();

	return 0;
}


运行结果:


如上所示:继承接口之后确实实现了深拷贝,但出现了一个新问题,克隆时返回的指针未正确析构,出现了内存泄露问题。

此时我们可以获取克隆时返回的指针,手动进行delete。

具体:

在简历类中提供一个方法CWorkExperience* getWorkExperience() 在需要释放内存时判断时是否为NULL 再进行释放。

返回指针:

// 避免实现深度拷贝之后的内存泄露问题
CWorkExperience* getWorkExperience()
{
	return m_experience;
}
手动释放:

// 解决深度拷贝之后的内存泄露问题(但很不友好)
	CWorkExperience* pWE = pClone->getWorkExperience();
	if (pWE)
	{
		delete pWE;
		pWE = NULL;
	}
运行结果:


上述思路是可以实现 深拷贝+ 无内存泄露 的,但是内存手动释放总是不友好。

而且如果clone对象不是深拷贝的话,会出现两次的内存释放,导致程序崩溃。


为了能够自动释放内存,我们可以采用第二种解决思路 —— 智能指针

2. 智能指针实现内存的自动析构

CResume类中的CWorkExperience指针我们可以用智能指针进行管理。

这样指针的所有权会自动管理。


代码如下:

#include <iostream>
#include <string>
#include <memory>

using namespace std;

template<class T>
class ICloneable
{
public:
	virtual T* clone() = 0;
};

class CWorkExperience:public ICloneable<CWorkExperience>
{
public:
	// 默认构造函数
	CWorkExperience(){}

	CWorkExperience(const string& company,
		const string& workTime)
	{
		m_strCompany = company;
		m_strWorkTime = workTime;
	}

	CWorkExperience(const CWorkExperience& right)
	{
		m_strCompany = right.m_strCompany;
		m_strWorkTime = right.m_strWorkTime;
	}

	~CWorkExperience()
	{
		cout << "CWorkExperience析构" << endl;
		printInfo();
	}

	CWorkExperience* clone()
	{
		// 这里是深拷贝
		CWorkExperience* pClone = new CWorkExperience(*this);
		return pClone;
	}

	void setCompany(const string& company)
	{
		m_strCompany = company;
	}

	const string& getCompany() const
	{
		return m_strCompany;
	}

	void setWorkTime(const string& workTime)
	{
		m_strWorkTime = workTime;
	}

	const string& getWorkTime() const
	{
		return m_strWorkTime;
	}

	void printInfo()
	{
		cout << "Company: " << m_strCompany << endl;
		cout << "WorkTime: " << m_strWorkTime << endl;
	}

private:
	string m_strCompany;		// company name
	string m_strWorkTime;		// work time
};

// 简历类
class CResume : public ICloneable<CResume>
{
private:
	// 只允许调用带参数的构造函数和拷贝构造函数
	CResume()
	{
		m_experience = NULL;
	}
public:
	// 带参构造函数
	CResume(CWorkExperience* pWorkExperience)
	{
		// 智能指针管理内存
		m_experience.reset(pWorkExperience);
	}

	// 带参拷贝构造函数
	CResume(const CResume& right)
	{
		m_name = right.m_name;
		m_sex = right.m_sex;
		m_age = right.m_age;

		// 智能指针管理内存
		m_experience.reset(right.m_experience->clone());
	}

	~CResume()
	{
		cout << "CResume析构 " << m_name<< endl;
	}

	void setInfo(const string& name, const string& sex, int age)
	{
		m_name = name;
		m_sex = sex;
		m_age = age;
	}

	void setExperience(const string& company, const string& workTime)
	{
		m_experience->setCompany(company);
		m_experience->setWorkTime(workTime);
	}

	CResume* clone()
	{
		CResume* resume = new CResume(*this);
		return resume;
	}

	void printInfo()
	{
		cout << "Name: " << m_name << endl;
		cout << "Sex: " << m_sex << endl;
		cout << "Age: " << m_age << endl;
		cout << "Experience: " << endl;
		m_experience->printInfo();
		cout << endl;
	}
protected:
	string m_name;
	string m_sex;
	int m_age;

	//使用智能指针
	tr1::shared_ptr<CWorkExperience> m_experience;
};

void testSmartPoint()
{
	CResume re(new CWorkExperience("MS", "2001.11 - 2005.11"));
	re.setInfo("Jacky", "Male", 20);
	re.printInfo();

	tr1::shared_ptr<CResume> pClone(re.clone());
	pClone->setInfo("Marry", "Female", 30);
	pClone->setExperience("Google", "2006.01 - 2010.01");
	pClone->printInfo();

	tr1::shared_ptr<CResume> pClone2(pClone->clone());
	pClone2->setInfo("maya", "Female", 40);
	pClone2->setExperience("Facebook", "2011.05 - 2014.05");
	pClone2->printInfo();
	pClone->printInfo();
	re.printInfo();
}

int main(void)
{
	testSmartPoint();

	return 0;
}

运行结果如图:(在新窗口中查看图片更清晰)


  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值