对象的浅拷贝和深拷贝

一、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)    
}

运行上面程序会崩溃

二、上述程序运行崩溃原因分析

image

原因分析
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;
}

image
解决原因分析
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,因为浅拷贝只拷贝类对象,而类对象只想的资源没有做拷贝会导致问题。

总结

一句话说明浅拷贝和深拷贝的区别
"浅拷贝时,对象本身的地址不同,但对象内部的指针成员指向同一块内存;深拷贝时,不仅对象地址不同,对象内部指针成员也指向不同的内存块,完全独立复制了所有资源。"

posted @ 2025-10-01 09:39  焦涛  阅读(9)  评论(0)    收藏  举报