1、引言。
今天复习到C++深拷贝和浅拷贝的知识点。现在,做个梳理。
2、浅拷贝。(反例)
2.1 实例。
#include <iostream>
using namespace std;
class Person
{
public:
Person(int nHeight, int nWeight);
~Person();
//我们自己来写系统生成的Default Copy Constructor
Person(const Person& pers);
private:
char *m_pName;
int m_nHeight;
int m_nWeight;
};
Person::Person(int nHeight, int nWeight)
{
m_nHeight = nHeight;
m_nWeight = nWeight;
m_pName = new char[20];
cout << "Constructor" << endl;
}
Person::~Person()
{
if (m_pName)
{
delete m_pName;
m_pName = NULL;
}
cout << "Destructor" << endl;
}
//自己来实现的默认拷贝构造函数。
Person::Person(const Person& pers)
{
this->m_nHeight = pers.m_nHeight;
this->m_nWeight = pers.m_nWeight;
this->m_pName = pers.m_pName;
}
int main()
{
#if _DEBUG
if (true)
{
Person zhangSan(170, 60);
Person wangEr(zhangSan);
}
#endif
cin.get();
return 0;
}
2.2 运行,崩溃。
2.3 小结。
/*
运行,崩溃。
第一句:调用zhangSan默认构造。第二句:调用wangEr默认拷贝构造。程序跳出if条件,两个对象生命周期结束,调用二者析构。
wangEr.m_pName指向了和zhangSan.m_pName指向的同一块地址。
这会出现什么情况呢?两个对象的指针成员指向了同一块内存,造成这块内存销毁两次,指针悬挂。
输出错误提示:_BLOCK_TYPE_IS_VALID(pHead->nBlockUse)。
搜索错误:VS调试_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse)); 参考资料: https://blog.csdn.net/mfcing/article/details/43022877
错误原因是同一块内存重复析构。
现在,我们考虑使用深拷贝。
*/
3、深拷贝。
3.1 实例。
#include <iostream>
using namespace std;
class Person
{
public:
Person();
Person(const Person& pers);
~Person();
private:
char *m_pName;
};
Person::Person()
{
m_pName = new char[20];
cout << "Default Constructor" << endl;
}
Person::Person(const Person& pers)
{
m_pName = new char[20];
memcpy(m_pName, pers.m_pName, strlen(m_pName));
cout << "Copy Constructor" << endl;
}
Person::~Person()
{
if (m_pName)
{
delete m_pName;
m_pName = NULL;
}
cout << "Destructor" << endl;
}
int main()
{
#if _DEBUG
if (true)
{
Person zhangSan;
Person wangEr(zhangSan);
}
#endif
cin.get();
return 0;
}
3.2 运行,成功。
3.3 小结。
第一句:调用对象zhangSan默认构造申请内存1。
第二句:调用对象wangEr自定义拷贝构造申请内存2。
程序跳出if条件,两个对象生命周期结束,调用二者析构,由于两个对象的指针分别指向两块内存,析构没有问题。
4、防止默认拷贝的发生。
4.1 分析思考。
通过对对象拷贝的分析,我们发现对象的拷贝大多在进行“值传递”时发生。
有一个小技巧可以防止按值传递——声明一个私有拷贝构造函数,不必去定义这个拷贝构造函数,这样因为拷贝构造函数是私有的。
如果用户试图按值传递或函数返回该类对象,将得到一个编译错误,从而避免按值传递或返回对象。
4.2 实例。
#include <iostream>
using namespace std;
class Person
{
public:
Person();
~Person();
private:
Person(const Person& pers);
private:
char *m_pName;
};
Person::Person()
{
m_pName = new char[20];
cout << "Default Constructor" << endl;
}
Person::~Person()
{
if (m_pName)
{
delete m_pName;
m_pName = NULL;
}
cout << "Destructor" << endl;
}
void g_TestFun(Person pers)
{
cout << "I'm test fun" << endl;
}
int main()
{
#if _DEBUG
if (true)
{
Person pers;
g_TestFun(pers);
}
#endif
cin.get();
return 0;
}
4.3 编译失败。
现在,我们达到目标。
5、总结。
5.1、编译器自动生成的拷贝构造函数,一般进行浅拷贝。
5.2、所谓浅拷贝,只是对指针的拷贝,与内存无关,拷贝后两个指针指向同一个内存空间。
5.3、我们自定义的拷贝构造函数应该是深拷贝。
5.4、所谓深拷贝,不但拷贝指针,而且需要拷贝指针指向的内存空间。
5.5、深拷贝作用后,造成结果:源对象指针和目标对象指针指向两块不同的内存空间,他们的析构由各自对象管理,不会发生指针悬挂。
5.6、深拷贝与浅拷贝的区别就在于深拷贝会在堆内存中另外申请空间来储存数据,从而也就解决了指针悬挂的问题。
5.7、当对象的数据成员中有指针时,必须要用深拷贝。
5.8、拷贝构造函数也具有普通成员函数重载性能。
比如:
class Person
{
Person::Person(const Person& pers);
Person::Person(const Person& pers,int num);
Person::Person(const Person& pers,char* p);
}
6、思考题。
思考题一:C++拷贝构造函数形参为什么不能传值,只能是引用?
//Answer:不是为了节省时间或者空间,而是为了防止无限递归。
6.1 我们思考这样一段代码:
class Person
{
public:
Person();
~Person();
Person(Person pers);
private:
char *m_pName;
};
Person::Person(Person pers)
{
this->m_pName = new char[20];
memcpy(this->m_pName, pers.m_pName, strlen(pers.m_pName));
cout << "Copy Constrcutor" << endl;
}
6.2 分析。
首先,Person persII = pers; 时,调用了值传递的Copy构造:persII.Person(pers);
接着,因为是指传递,需要先拷贝创建一个pers的对象副本pers_s,那就得要调用copy构造,pers_s.Person(pers);
现在,进入了死循环,无限调用copy构造pers_s.Person(pers);
事实上,编译器时不允许我们的拷贝构造函数形参传值的。
我们来看这段代码:
#include <iostream>
using namespace std;
class Person
{
public:
Person();
~Person();
Person(Person pers);
private:
char *m_pName;
};
Person::Person()
{
m_pName = new char[20];
cout << "Default Constructor" << endl;
}
Person::~Person()
{
if (m_pName)
{
delete m_pName;
m_pName = NULL;
}
cout << "Destructor" << endl;
}
Person::Person(Person pers)
{
this->m_pName = new char[20];
memcpy(this->m_pName, pers.m_pName, strlen(pers.m_pName));
cout << "Copy Constrcutor" << endl;
}
int main()
{
#if _DEBUG
if (true)
{
Person pers;
Person persII = pers;
}
#endif
cin.get();
return 0;
}
编译失败:
思考题二:C++拷贝构造函数形参为什么不传指针,而去传引用?
个人猜测:引用比指针更安全。引用必须有依赖对象,但是指针可能悬挂。
入门萌新,浅知拙见,若有斧正,不胜感激。^ - ^