【C++】理解C++中的复制、复制构造函数

十、理解C++中的复制、复制构造函数

拷贝就是要复制数据,也就是复制内存。
当我们把一个对象或一段数据从一个地方拷贝到另一个地方,那这个对象或数据其实是有两个副本,而且这个过程还是需要时间和开销的。所以如果你只是想读取数据,那你就要尽量避免拷贝;如果你还需要修改已经存在的数据,而且还想保留源数据,此时才需要拷贝。总之就是要避免不必要的拷贝,因为这会浪费性能。所以从底层了解复制是如何运作的,才可以避免不必要的浪费,才能高效正确的编写C++代码。

本篇先讲什么是copy,然后手写一个字符串复制的类,揭开拷贝的本质,就是拷贝都做了什么工作,并从中引出什么是浅拷贝、深拷贝。然后再从深拷贝引出复制构造函数,最后总结什么时候该拷贝什么时候不需要拷贝,不需要拷贝时如何阻止或者减少拷贝,让你的程序运行更快。因为有些拷贝不仅仅是复制点数据、浪费几个cpu周期,而是彻头彻尾的不需要。

1、展示三个copy的例子:

可见除了引用外,只要你是赋值=号就基本上就是copy了。

2、自己手动写一个字符串复制的类,看看copy是如何实现的:

从上图可见,我们实现copy就是一个个字符的拷贝,把常量区的字符串复制一份到堆中。下面我再以打断点的方式,看看是不是内存中确实是“liyuanyuan”和"LiyuanyuanE"是分别存储在两个地方:

此处的内容如果还有疑问,可参考 【C++】深度理解C++数据类型:常量、变量、数组、字符串、指针、函数_c++ 字符串常量-CSDN博客 中的字符串字面量部分,你会更加清晰。

3、浅拷贝、深拷贝
但是上图的代码是初始化一个对象a,如果我们用=号赋值一个对象呢?就是用=号能不能复制一个对象b?如下图所示我这样写main函数: 

很不幸,当我们用=号复制时,就出现了崩溃!
为什么会出现上面的异常?那是因为,当我们把对象a赋值给对象b时(上图A处),对象a的其实不是"liyuanyuanE"这个字符串数组本身。对象a只是{p, m_size},就是对象a在内存中存储的是一个指针p和一个unsigned int类型的m_size。所以我们A处其实就是把p和m_size拷贝给了b。也所以打印b其实打印的是b.p,而a的p和b的p的值都一样,都是指向堆上的同一块儿内存。也所以当b修改了第一个元素为L,a打印出来其第一个元素也被修改了。也所以当代码执行到B处,先释放对象b时,就已经调用了析构函数删除了堆上的字符串数组了,再释放对象a时,又得调用析构函数,此时a中的p的值指向的堆内存已经被标记为free状态了,你再调析构函数free那块内存就没法free了,于是报了_UNKOWN_BLOCK。

从这个崩溃中我们可以看出:除了引用赋值,其他赋值我们都可以称为复制,但是有的是复制本体,比如对象a就是真真切切把常量区的字符串复制了一份到堆区;而有的则是复制了一个指向这个对象的指针!,比如对象b复制的不是a的堆中的字符串数组,而是栈中的a的本体:一个指针p一个无符号整型m_size。所以,我们称a的复制就是深拷贝;b的复制就是浅拷贝。二者都是拷贝,但是一个拷贝了本体,一个拷贝了指向本体的指针,而没有拷贝指针指向的内容。

那如何让上面的b也真真切切的拷贝一份字符串而非一个指向字符串的指针呢?你可以依然用函数或者方法去写,让b也返回的是一个新字符串,但是这里我们不介绍这种方法,我们介绍另外一种方法:如何写复制构造函数

4、复制构造函数
复制构造函数也是一个构造函数,当你复制第二个字符串时,它会被重载。当你把一个字符串赋值给另一个字符串对象时,就是当你试图创建一个新的变量并给它分配另一个同类型变量时,你复制这个变量时,就会自动调用拷贝构造函数。C++默认情况下会为我们提供一个复制构造函数,但是这里我们先自己手动写一个简易版的,看看它是怎么运行的。
ps,此时你明白了我为何不用C++自带的string库了嘛,因为如果我使用了,很多东西就被在我们看不见的地方自动化执行了,我们就看不到事物的本来面貌了。

至此,我们的程序就正常工作了,不会出现a,b互相影响的情况,也不会出现程序崩溃的现象,程序成功终止。

5、想复制就复制吗?
当我们把上面的代码中的cout全部用一个函数代替时:

可见,不是任何时候我们都需要拷贝的,上例中的func函数,调用了3次,就拷贝了3次参数,调用完毕还得销毁,所以也销毁了3次。要知道每次拷贝都是要在堆上另辟一块内存,一个个字符逐个写入的。而func仅仅是读一下打印出来,然后还得销毁。这岂不是非常浪费吗?完全没必要这样做啊!我们可以这样做:

上面的做法就是想告诉我们always alwayst一定要通过const引用去传递对象。因为在基础使用中,const引用更好。即使你在函数内部想复制参数,那你就在函数内部复制,不需要到处复制,到处复制会拖慢你的程序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值