对象的浅拷贝和深拷贝
一、OOP实现一个顺序栈
/*
OOP实现一个顺序栈
*/
class SeqStack
{
public:
/*
* 析构函数
*/
SeqStack(int size = 10)//是可以带参数的,因此可以提供多个构造函数
{
std::cout << this << "SeqStack" << std::endl;
_pstack = new int[size];
_top = -1;
_size = size;
}
/*
* 析构函数
*/
~SeqStack()//是不带参数的,所以析构函数只能有一个
{
std::cout << this << "~SeqStack" << std::endl;
delete[] _pstack;
_pstack = nullptr;
}
void push(int val)
{
if (full())
resize();
_pstack[++_top] = val;
}
void pop()
{
if (empty())
return;
--_top;
}
int top()
{
return _pstack[_top];
}
bool empty() {
return _top == -1;
}
bool full()
{
return _top == _size - 1;
}
private:
int* _pstack;//动态开票数组,存储顺序栈的元素
int _top;//指像栈顶元素的位置
int _size;//数据扩容的总大小
int* _pstack;
int _top;
int _size;
void resize()
{
int* ptmp = new int[_size * 2];
for (int i = 0;i < _size;++i)
{
ptmp[i] = _pstack[i];
}
delete[] _pstack;
_pstack = ptmp;
_size *= 2;
}
};
int main()
{
SeqStack s1(20);
SeqStack s2 = s1;//默认拷贝构造,做直接做内存拷贝
//SeqStack s2(s1);//默认拷贝构造拷(等同SeqStack s2 = s1)
}
运行上面程序会崩溃
二、上述程序运行崩溃原因分析

原因分析
1.SeqStack s2 = s1;//默认拷贝构造函数(浅拷贝),做直接做内存拷贝,导致s2和s1中的pstack指向相同地址
2.当析构s1析构函数调用delete[] _pstack,这是_pstack成为野指针,当析构s2析构函数也会调用delete[] _pstack,而这是_pstack是野指针再次delete程序就会崩溃
三、解决办法
添加自定义拷贝构造函数
//自定义拷贝构造函数
SeqStack(const SeqStack& src)
{
std::cout << "SeqStack(const SeqStack &src)" << std::endl;
_pstack = new int[src._size];
for (int i = 0;i < src._top;++i)
{
_pstack[i] = src._pstack[i];
}
_top = src._top;
_size = src._size;
}

解决原因分析
1.SeqStack s2 = s1;//自定义拷贝构造函数(深拷贝), _pstack = new int[src._size],使得s2和s1中的pstack指向不同地址
2.当析构s1析构函数中调用delete[] _pstack,这是_pstack成为野指针,当析构s2析构函数也会调用delete[],而s2指向不同于s1的 _pstack,所以s2的_pstack指针delete正常释放
输出结果如下
0000001DC2CFF698SeqStack
0000001DC2CFF6C8SeqStack(const SeqStack &src)
0000001DC2CFF6C8~SeqStack
0000001DC2CFF698~SeqStack
四、s2=s1赋值问题
如果要实现s2=s1,调用上面代码仍会报错
int main()
{
SeqStack s1(20);
SeqStack s2 = s1;//默认拷贝构造,做直接做内存拷贝
//SeqStack s2(s1);//默认拷贝构造拷(等同SeqStack s2 = s1)
//s2.operator(s1)
//void operator=(const SeqStack &src)
s2 = s1;//赋值操作;会调用默认赋值函数,做直接做内存拷贝
}
原因分析
赋值操作;会调用默认赋值函数,做直接做内存拷贝
解决办法
添加自定义赋值函数
//赋值重载函数s2=s1
void operator=(const SeqStack& src)
{
std::cout<<this << "operator="<<std::endl;
//防止自赋值
if (this == &src)
{
return;
}
//需要先释放当前对象占用的外部资源
delete _pstack;
_pstack = new int[src._size];
for (int i = 0;i < src._top;++i)
{
_pstack[i] = src._pstack[i];
}
_top = src._top;
_size = src._size;
}
五、为什么不用memcpy进行数内存拷贝
for (int i = 0;i < _size;++i)
{
ptmp[i] = _pstack[i];
}//memcpy(ptmp,_pstack,sizeof(int)*_size);
这里其实是可以使用 memcpy。因为这里数组是int类型,如果是数组中存放的是类对象而且每个对象指针指向外部资源,就不能使用memcpy,因为浅拷贝只拷贝类对象,而类对象只想的资源没有做拷贝会导致问题。
总结
一句话说明浅拷贝和深拷贝的区别
"浅拷贝时,对象本身的地址不同,但对象内部的指针成员指向同一块内存;深拷贝时,不仅对象地址不同,对象内部指针成员也指向不同的内存块,完全独立复制了所有资源。"
浙公网安备 33010602011771号