浅拷贝
浅拷贝主要是指:位拷贝,拷贝构造函数,赋值重载
浅拷贝会把指针变量的地址复制,多个对象共用同一块资源,同一块资源释放多次,崩溃或者内存泄漏。
具体请看下面的例子:
#define SEQ_INIT_SIZE 10
#define SEQ_INC_SIZE 2
class SeqList
{
int *data;
int maxsize;
int cursize;
public:
SeqList() :data(NULL),maxsize(SEQ_INIT_SIZE), cursize(0)
{
data = (int*)malloc(sizeof(int) * maxsize);
}
~SeqList()
{
free(data);
data = NULL;
}
SeqList & operator=(const SeqList & x)
{
if (this != &x)
{
data = x.data;
maxsize = x.maxsize;
cursize = x.cursize;
}
return *this;
}
SeqList(const SeqList& y):data(y.data),maxsize (y.maxsize),cursize(y.cursize){}
};
int main()
{
SeqList seqa;
SeqList seqb(seqa);
}
此时程序会崩溃,原因是:
在完成拷贝构造后,两个对象的data域
都指向了同一块堆区空间,随着函数执行结束,seqb
会调动自身的析构函数,释放资源,并回收空间,此时seqa
调动自身的析构函数时,data域已经指向了一块失效空间,并进行了二次释放,程序崩溃。
int main()
{
SeqList seqa;
SeqList seqb;
seqb = seqa;
}
此时程序依然会崩溃,原因有二:
- 调动构造函数后各自的
data域
指向各自的堆区空间,然后完成赋值后,seqb
的data域
转而指向seqa
的data域
所指向的堆区空间,会造成二次释放。 - 同时,由于未对
seqb
的data域
所指向的内存空间进行管理,造成了内存泄漏。
问题:那么什么时候,我们需要自己重写赋值语句和拷贝构造函数呢?
凡是在类设计的过程中,设计出指针或者设计出指向内核态的对象时,都必须重写自己的拷贝构造和赋值语句,比如文件句柄指针,线程id号信号量,互斥量,不允许这两种操作时,加上
=delete
即可。
因此,在我们了解了对象在内存中的分布情况后,我们就有了更加深刻的领悟,也就有了如下的深拷贝
的方案。
深拷贝
深拷贝会重新开辟内存空间,每个对象共同拥有自己的资源。
SeqList & operator=(const SeqList & x)
{
if (this != &x)
{
free(data);//释放原有空间
data = (int*)malloc(sizeof(int) * x.maxsize);
memcpy(data, x.data, sizeof(int) * x.cursize);
maxsize = x.maxsize;
cursize = x.cursize;
}
return *this;
}
SeqList(const SeqList& y):maxsize (y.maxsize),cursize(y.cursize)
{
data = (int*)malloc(sizeof(int) * y.maxsize);
memcpy(data, y.data, sizeof(int) * y.cursize);
}
};
采用深拷贝方案后,修改代码如下:此时各自的堆区空间都得到了合法的使用和销毁。