C++的拷贝构造当中,一旦涉及到堆内存的分配,就会出现,两个指针指向同一个内存的情况,但是一旦原来的内存只释放自己的内存,那么剩下的就悬空了。
因此只要堆内存分配,需要考虑四个内容:
1、构造
2、析构
3、拷贝构造
4、赋值运算符
这四个内容缺一不可。
拷贝构造:
在拷贝构造的时候(前处理/逐字节拷贝),只拷贝栈中的内容,不会管堆中的内容有多大,因此,如果使用了堆内存分配,再进行拷贝构造的时候,要考虑重新给新生成的堆内存的地址指定新的内存空间(后处理),否则就会出现内存被释放两次的情况,而且如果前面的那个对象被释放,后面新生成的对象是拿不到堆中的数据的。
赋值运算符:
在内存中的也是逐字节拷贝,当前一个对象释放的时候,他在堆中指向的那个空间先被释放掉,随后指向NULL,现在由于,将ma的内容拷贝到ma2当中,因此ma2也指向ma在堆内存中指向的那个空间,因此,当ma2被释放的时候,将会发生内存重复释放,内存崩溃,同时,原先的ma2指向的那个对空间由于已经找不到他的地址了,所以发生内存泄漏。
对象的赋值运算符,只能是成员,不能是友元
#include <iostream>
using namespace std;
class MyArray
{
public:
MyArray(int len = 1):len(len)
{
//由于初始化构造比len的内部参数要早,因此为了保险起见,将
//赋值的地方放在内部
pdatas = new int[len];
};
~MyArray()
{
delete[] pdatas;
pdatas = NULL;
};
//拷贝构造要使用引用才可以
MyArray(const MyArray& ma)
{
len = ma.len;
pdatas = new int[len];
//使用C++库的memcpy
//这里不能使用this因为this当中还包含len的相关内容
//因此使用pdatas的
memcpy(this->pdatas, ma.pdatas, sizeof(int) * len);
}
MyArray& operator=(MyArray& ma)
{
//判断是不是自己赋值给自己
//先申请一段内存 拷贝被复制对象的内存数据
//释放自己的内存
//让指针指向新内存
if (this != &ma)
{
len = ma.len;
//这里需要考虑一下,如果是先申请一段内存的话
//不能直接使用pdatas来申请内存,
//因为如果使用pdatas来申请内存,那么原先pdatas指向的那块内存就找不到了
//就出现内存泄露了
int* ptemp = new int[len];
memcpy(ptemp, ma.pdatas, sizeof(int) * len);
delete[] pdatas;
pdatas = ptemp;
}
return *this;
}
private:
int* pdatas;
int len;
};
int main()
{
MyArray ma(10);
MyArray ma2(ma);
ma2 = ma;
}