C++复习之深拷贝与浅拷贝

浅拷贝

还是顺序栈


#include <iostream>
using namespace std;

class SeqStack
{
public:
   //构造函数
   SeqStack(int size = 10)
   {
   	cout << this << " SeqStack() " << endl;
       _pstack = new int[size];
       _top = -1;
       _size = size;
   }
   //析构函数
   ~ SeqStack()
   {
   	cout << this << " ~SeqStack() " << 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;//数组扩容的总大小
   void resize()
   {
       int *ptmp = new int[2 * _size];
       for (int i = 0; i < _size; ++i)
       {
           ptmp[i] = _pstack[i];
       }
       delete[]_pstack;
       _pstack = ptmp;
       _size *= 2;
   }
};

int main()
{
   SeqStack s;//没有提供任何构造函数时,会默认构造与默认析构,是空函数
   SeqStack s1(10);
   SeqStack s2 = s1;//#1 默认拷贝构造函数,做直接内存数据拷贝
   //SeqStack s3(s1);#2
   return 0;
}

在main函数中,#1与#2都调用了拷贝构造函数 。

这个程序会运行失败,原因是先构造出s1对象,再利用s1构造s2对象时,只是简单的将s1里成员变量的值复制到了s2里,s1与s2中_pstack成员同指向一块堆空间。而当析构时,s2先析构,s2中_pstack所指向的堆空间内存被释放掉了。但是s1中_pstack成员变量所存的依然是这块内存的地址,但是这块内存已经被释放掉了,s1中_pstack成了一个野指针,当s1析构时,就会出现一个释放野指针的操作,程序当然就挂掉了。这就是浅拷贝,也就是对象直接进入内存拷贝。
在这里插入图片描述
并不是所有的浅拷贝都会出错,当对象中成员对象,尤其是指针指向了对象之外的资源时,当发生浅拷贝时,导致两个对象的指针指向了同一个资源。第一个对象析构时,把这个资源释放,第二个对象析构时,就会出错,因为同一个资源不能释放两次。
1.对象默认的拷贝构造是做内存的数据拷贝。
2.如果对象占用外部资源,浅拷贝会出问题。这时候该使用深拷贝!
3.如果发生浅拷贝,则需要自定义拷贝构造函数和赋值重载函数。

深拷贝

就是当发生拷贝时,对象的成员变量不只只把值复制下来,如果对象的成员变量有指针指向外部资源,应该给拷贝对象的成员变量也开辟一个外部资源,让新对象的指针指向这个外部资源。这样,每个对象的指针都指向不同的外部资源,析构时也就互不影响。


#include <iostream>
using namespace std;

class SeqStack
{
public:
   //构造函数
   SeqStack(int size = 10)
   {
   	cout << this << " SeqStack() " << endl;
       _pstack = new int[size];
       _top = -1;
       _size = size;
   }
   //自定义拷贝构造函数(深拷贝),因为对象的浅拷贝出了问题
SeqStack(const SeqStack &src)
{
   cout << "SeqStack(const SeqStack &src)" << 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;
}
   //析构函数
   ~ SeqStack()
   {
   	cout << this << " ~SeqStack() " << 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;//数组扩容的总大小
   void resize()
   {
       int *ptmp = new int[2 * _size];
       for (int i = 0; i < _size; ++i)
       {
           ptmp[i] = _pstack[i];
       }
       delete[]_pstack;
       _pstack = ptmp;
       _size *= 2;
   }
};

int main()
{
   SeqStack s;//没有提供任何构造函数时,会默认构造与默认析构,是空函数
   SeqStack s1(10);
   SeqStack s2 = s1;//#1 默认拷贝构造函数,做直接内存数据拷贝
   //SeqStack s3(s1);#2
   return 0;
}

可以看到
在这里插入图片描述
可以看到,程序运行没有问题,构造三次,析构三次。

赋值重载函数

与深拷贝的区别在于,深拷贝需要重新构造对象,而拷贝赋值中,对象已经存在了,只是将一个对象里的值拷贝给另一个对象。

int main()
{
	SeqStack s1(10);
	SeqStack s2 = s1;
	s2 = s1;//赋值操作,也属于内存拷贝
	return 0;
}
//赋值拷贝函数
void operator=const SeqStack &src)
{
	cout << " operator=" << 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;
}

在这里插入图片描述
可以看到实现了拷贝赋值操作。
总结下来拷贝赋值主要有三步:
1.防止自赋值
2.释放当前对象占用的外部资源
3.进行拷贝构造(深拷贝)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值