将某种信息/数据从一个位置传递到另一个位置是程序中的常用操作,这一过程可以被视为(广义的)拷贝。在编写程序中,通常会涉及到两种形式的拷贝:
可以看出,深浅拷贝的区分主要是针对指针而言。显然,后一种方法没有实现真正意义的复制,在进行浅拷贝后,对于后面数据的修改会影响到之前的原始数据,带来难以发现的影响。
在编写程序中,根据实际需要,这两种方式都会被用到,合理的使用拷贝可以有效利用资源。但在实际场景中,由于不清楚这两种操作的界限,在许多情况下会造成复杂的问题,C++设计了一些机制来约束自定义类中的拷贝行为。
在正式开始介绍拷贝之前,需要关注几个与之相关的概念:
可以看出,第一种情况是较为引起注意的拷贝,但是后两种情况拷贝也会发生,并且很容易被忽略。
引用可以认为是原有数据的一种别名,它并不占用新的数据空间(此处是指不会占用与被引用数据一样大的空间)。引用可以通过一种类似常量指针的方式实现的,在引用被声明的时候就需要进行初始化。
通常不认为传递/返回引用的过程中进行了拷贝,即引用是我们所说的pass-by-reference,而拷贝指的是pass-by-value。
在许多时候,一个对象被拷贝之后很快就被销毁或不再使用了,那么这个拷贝操作显得有些浪费资源。在这种情况下,可以使用移动操作避免拷贝。为了定义移动操作,需要在程序中区分右值和左值。
左右值的区别主要在于所表示对象的生命周期不同,左值是一个具有持久状态的对象(变量),右值是一个字面量或者临时对象。该右值对象与其他对象毫无联系,可以被销毁或者进行变动,不会带来其他负面影响。因此,移动操作主要是针对右值进行的。
我们所说的普通引用可以被视为左值引用,在引入右值的概念后,相对应的也有右值引用,右值引用只能绑定到字面量或者返回右值的表达式上,而不能绑定到左值上。(但是反过来是可以的)
移动的目的,就是将一个左值转化为右值引用从而避免拷贝。在这种情况下,需要符合之前的承诺,即完全切断源对象与其他对象的联系,当作真正的右值来处理。此后源对象只能销毁或者赋新值,这一操作应当由程序设计者保证。
在一个自定义类中,有以下几个特殊的成员函数与拷贝行为有关:
在默认情况下,C++会为自定义类合成这几个特殊的成员函数,而根据类功能和设计不同,以上一些默认方法合成的函数可能会带来意外危害。
三五法则是一个C++中对以上成员函数设计策略的建议。首先这里要说明,Rule of Three/Five并不是法则的内容有三条或者五条,而是为了说明这个法则针对的对象是三个函数(以上2-4)还是五个函数(2-6),后者是C++11后开始支持的。三五法则的实质就是为了对拷贝行为进行控制,避免潜在的问题。
法则具体内容可以参考:https://smartkeyerror.oss-cn-shenzhen.aliyuncs.com/Phyduck/c%2B%2B/copy-control/4.%20%E4%B8%89%E4%BA%94%E6%B3%95%E5%88%99.pdf
简要梳理一下要做的事,实际上就是区分几种操作,以及认识三种操作应当在什么场景下进行。
传递信息的操作分别为:
不同情况需要不同的场景支持,这个并不是绝对的,需要个人结合情况分析。
C++复习——降低拷贝代价(深/浅拷贝,左值/右值引用,拷贝构造和移动操作)
原文:https://www.cnblogs.com/hesun/p/15111701.html