上一篇文章指出了原型模式中浅拷贝时出现的一些问题,如内存泄露等问题。
下面给出几种可能的解决方案来避免内存泄露。
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;
}
运行结果如图:(在新窗口中查看图片更清晰)