c++ 浅拷贝与深拷贝


前言

浅拷贝与深拷贝是c++中一个非常重要的点,也是面试经典问题,如果对这2个操作没有深入的理解,那么开发中的代码就会存在潜在的内存危险


一、浅拷贝与深拷贝的时机

深浅拷贝的时机是:拷贝内容中有对指针的拷贝

二、深浅拷贝的详细理解

1.浅拷贝

浅拷贝是由编译器默认完成的操作,它的拷贝过程是逐bit位拷贝
假设有如下一个string类:

class string
{
	public:
		string(const char* cstr=0);
		string(const string& str);//拷贝构造 copy constructor
		string& operator=(const string& str);// 拷贝赋值  copy assignment
		~string();
	private:
		char* m_date;
}

编译器提供的默认拷贝与析构:

//拷贝构造
string::string(const string& str)
{
	this.m_date=str.m_date;
	//strcpy(this.m_data,str.m_data);
}
//拷贝赋值
string& string::operator=(const string& str)
{
	strcpy(this,str);
	return *this;
}
//析构函数
string::~string()
{
	if(this.m_date!=null)
	{
		delete this.m_date;
		this.m_date=null;
	}
	
}

那么,深浅拷贝,就发生在拷贝构造与拷贝赋值操作中。

  • 拷贝赋值
    此时有2个string类对象,对象a和对象b,即:string a("hello"); string b("world");,现在进行操作,把对象a拷贝给对象b,代码如b=a;(拷贝赋值),对象b和a都是使用指针指向内存中的字符串,那对象中存放的是字符串所在内存的内存地址,编译器进行浅拷贝,也就是逐bit位的把对象a的内存地址拷贝给对象b,此时通过分析图看出,b对象原先的内存发生了泄露,对象b对自己内存数据的修改(即:对“hello”的修改),也会影响到对象a,而我们拷贝的原意是对象b有对象a的所有属性,且二者对自己内存的操作是独立性的。所以,拷贝操作,把自己的指针指向了被拷贝者的内存的拷贝方式,就称作浅拷贝
    浅拷贝之拷贝赋值
  • 拷贝构造
    此时有一个string类a,再创建一个string类b,使用拷贝构造创建,代码如:string a("hello");string b(a);当工作完成后,调用析构函数时,会发生内存错误,由分析图可知,先调用b的析构函数,字符串“hello”所在内存被释放,接着调用a的析构函数,此时字符串“hello”所在内存已经被释放了,再对“hello”内存进行释放就发生错误。
    浅拷贝之拷贝构造
  • 浅拷贝总结
    浅拷贝是编译器的默认拷贝操作,是值的逐bit位拷贝,存在以下潜在危险不等:
  1. 内存泄露(memory leak)
  2. 对象不再具有独立性
  3. 析构错误

2.深拷贝

深拷贝是为了解决浅拷贝潜在危险的另一种拷贝方式,它的思想是把被拷贝者的数据复制一份到自己的内存空间,拷贝者与被拷贝者对自己内存空间进项操作,仍具有独立性,如:有一个string类对象a,现在对象b要对a进行拷贝,情况分为拷贝构造与拷贝赋值,分析如下:

  • 拷贝构造
    操作代码如下:
string a("hello");
string b(a);

分析图:
深拷贝之拷贝构造

深拷贝之拷贝构造代码实现如下:

//深拷贝构造
string::string(const string& str)
{
	this.m_date=new char[strlen(str.m_date)+1];
	stpcpy(this.m_date,str.m_date);
}
  • 拷贝赋值

操作代码如下:

string a("hello");
string b("world");
b=a

分析图:

深拷贝之拷贝赋值
操作代码如下:

//深拷贝赋值
string& operator=(const string& str)
{
	//自我赋值检测
	if(ths==&str)
		return *this;
	//释放原来的内存
	if(m_date!=null)
	{
		delete [] m_date;
		m_date=null;
	}
	m_date=new char[strlen(str.m_date)+1];
	strcpy(m_date,str.m_date);
	return *this;
}

自我赋值检测的重要性
拷贝赋值的过程:

  1. 释放自己的内存空间
  2. 开辟拷贝数据的内存空间
  3. 把数据拷贝进开辟的内存空间

假设没有自我赋值检测,有如下情况:对象a和对象b的m_data都是想同一内存,内存中存放字符串“hello”,现在我们按照拷贝赋值的过程开始走,前2步都是正常的,但到第3步就会报错,原因:a和b都是指向同一内存空间,第1步把这个内存空间释放了,在第3不拷贝数据时,就找不到拷贝的数据源,所以发生错误


总结

浅拷贝是编译器提供的方式,适合大多数的拷贝操作,但涉及到指针的拷贝,就必须使用深拷贝,归根结底,是指针与内存的理解。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值