1. 程式人生 > >設計模式之 原型(Prototype)模式(使用智慧指標避免淺拷貝時記憶體洩露)

設計模式之 原型(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;
}

執行結果如圖:(在新視窗中檢視圖片更清晰)