設計模式之 原型(Prototype)模式(使用智慧指標避免淺拷貝時記憶體洩露)
阿新 • • 發佈:2019-02-01
上一篇文章指出了原型模式中淺拷貝時出現的一些問題,如記憶體洩露等問題。
下面給出幾種可能的解決方案來避免記憶體洩露。
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; }
執行結果如圖:(在新視窗中檢視圖片更清晰)