本文摘自网络,作者php是最好的语言,侵删。
对于普通的类型来说,拷贝没什么大不了的。
不会出现任何问题。
而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量。
浅拷贝
首先来说说我们常遇到的浅拷贝的情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #include <stdio.h> class student
{public:
student() // 构造函数,p指向堆中分配的一空间
{
_name = new char( 100 ); printf( "默认构造函数\n" );
}
~student() // 析构函数,释放动态分配的空间
{ if (_name != NULL)
{ delete _name;
_name = NULL; printf( "析构函数\n" );
}
}private: char * _name; // 一指针成员};int main()
{
student a;
student b(a); // 复制对象
return 0 ;
}
|
这段代码乍看之下没什么毛病,通过类的默认构造函数将 a 复制给 b ,但是一旦运行就会程序崩溃。
经过我的刻苦学习与钻研,终于发现其中的问题所在。
由于我的类没有拷贝构造函数,所以student b(a)
会调用,编译器自动生成的一个默认拷贝构造函数,该构造函数完成对象之间的位拷贝。位拷贝又称浅拷贝。
浅拷贝:
浅拷贝只是拷贝了指针,并没有创建新的空间,使得两个指针指向同一个地址,这样在对象块结束,调用函数析构的时,会造成同一份资源析构2次,即delete同一块内存2次,造成程序崩溃。
浅拷贝使得 a 和 b 指向同一块内存,任何一方的变动都会影响到另一方。
由于 a 和 b 指向的是同一块内存空间,当 a 释放了后,b 指向的内存空间不复存在,所以会出现内存泄露的情况。
如何避免浅拷贝害人呢?
养成自定义拷贝构造函数的习惯,当显式定义了拷贝构造函数后,编译器就会调用拷贝构造函数了,为了不出现程序崩溃,请使用自定义拷贝构造函数,当然我们自己如果把代码写成了浅拷贝的形式,那也不是不可能的事。
深拷贝
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | // 使用自定制拷贝构造函数,完成深拷贝!!!class A
{public:
A() // 构造函数,p指向堆中分配的一空间
{
m_pdata = new char( 100 ); printf( "默认构造函数\n" );
}
A(const A& r) // 拷贝构造函数
{
m_pdata = new char( 100 ); // 为新对象重新动态分配空间
memcpy(m_pdata, r.m_pdata, strlen(r.m_pdata)); printf( "copy构造函数\n" );
}
~A() // 析构函数,释放动态分配的空间
{ if (m_pdata != NULL)
{ delete m_pdata; printf( "析构函数\n" );
}
}private: char *m_pdata; // 一指针成员};int main()
{
A a;
A b(a); // 复制对象
return 0 ;
}
|
在拷贝构造函数中,为 b 对象 new 了一个新的空间,这样 a 和 b 指向的是不同的空间,只是内容一致,但是互不影响。
重复的去开辟空间和释放空间效率是很低的,聪明的地球人决定使用写时拷贝。
写时拷贝
写时拷贝:引入一个计数器,每片不同内容的空间上都再由一个计数器组成,在构造第一个类指向时,计数器初始化为1,之后每次有新的类也指向同一片空间时,计数器加 1 ;在析构时判断该片空间对应计数器是否为1,为1则执行清理工作,大于1则计数器减 1 。如果有需要进行增删等操作时,再拷贝空间完成,有利于提高效率。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | class String
{
public:
String(const char* str = "" )
:_str(new char[strlen(str) + 1 + 4 ])//+ 1 表示字符串后面要放一个 '\0' ,+ 4 表示多开辟一个空间存放引用计数
{ _str += 4 ;//_str指向数据存放区
strcpy(_str, str); _GetCount() = 1 ;
}
String(const String& s)
:_str(s._str)
{ _GetCount()++;
}
String& operator=(String& s)
{ if (this != &s)
{ if (--_GetCount() == 0 )
{
delete[](_str - 4 );
}
++s._GetCount(); _str = s._str;
} return *this;
}
~String()
{ if (--_GetCount() == 0 )
{
delete[](_str - 4 ); // 注意:由于计数器存放在了_str首地址 -4 的地址上,所以在析构时一定要注意全部释放,避免内存泄漏。
}
}
public: int& _GetCount()
{ return *((int*)_str - 1 );
}
private: char* _str;
};
|
相关文章:
C#浅拷贝和深拷贝实例解析
Python中的赋值、浅拷贝、深拷贝介绍
以上就是C++---浅拷贝、深拷贝、写时拷贝讲解(附代码)的详细内容!
相关阅读 >>
c++---浅拷贝、深拷贝、写时拷贝讲解(附代码)
更多相关阅读请进入《深拷贝》频道 >>
转载请注明出处:木庄网络博客 » C++---浅拷贝、深拷贝、写时拷贝讲解(附代码)