C++ Copy Control笔记(1):Copy, Assign, and Destroy

什么是Copy Control

在C++中,我们可以对类自定义拷贝构造器(copy constructor), 移动构造器(move constructor), 拷贝赋值运算符(copy-assignment operator),移动赋值运算符(move-assignment operator), 析构器(destructor)等方法。我们将这些方法统称为Copy Control

拷贝构造器(The Copy Constructor)

C++的每一个类都有一个拷贝构造器用于构造对象,其参数为同类型另一个对象的引用,这个参数几乎都是const的

合成拷贝构造器(The Synthesized Copy Constructor)

如果我们不自己定义一个Copy Constructor,那么编译器会自动生成一个合成的(Synthesized) Copy Constructor,与默认构造器不同的是,即使我们定义了其他构造器,编译器还是会自动生成Copy Constructor。

合成的拷贝构造器会递归调用成员函数的合成构造器,但是它无法直接拷贝数组

eg. 合成的拷贝构造器与下面代码展示的拷贝构造器相同

拷贝初始化(Copy Initialization)

C++中变量的初始化分为拷贝初始化(Copy Initialization)和直接初始化(Direct Initialzation), 显式的使用构造器进行的初始化为直接初始化,使用=运算符进行的初始化为拷贝初始化。

eg.

使用拷贝初始化时一般会直接调用拷贝构造器(*如果类拥有移动构造器,则在拷贝初始化时会使用移动构造器)

此外,拷贝初始化还会在以下场景中被使用

  • 在function中传入非引用对象(nonreference object type)作为参数

  • 在function中将非引用对象作为参数返回

  • 大括号初始化数组中的元素或是聚合类的成员

*有些类还会在类分配内存时使用拷贝初始化

拷贝初始化的限制(Constraints on Copy Initialization)

在C++中,我们无法隐式的使用类创造的显式构造器

因此在例子中,v2无法成功初始化

*在进行拷贝初始化时,编译器可以选择使用直接构造对象从而跳过拷贝或移动构造器

eg.

然而,即使编译器选择跳过拷贝或移动构造器,此拷贝或移动构造器还是必须存在并可以访问(eg. not private)

拷贝赋值运算符(The Copy-Assignment Operator)

在C++中,正如类控制着对象是如何被创建的,它同时也控制着对象是如何被赋值的。

同样的,如果类没有定义一个赋值运算符,编译器会自动生成一个合成的运算符

重载运算符(Overloaded Operators)

在C++中,我们可以重载一个类中的运算符,例如operator+, operator==, operator=等等

eg. 重载拷贝运算符

合成的拷贝赋值运算符(The Synthesized Copy-Assignment Operator)

与Copy Constructor类似,合成的拷贝赋值会将递归调用成员变量的拷贝赋值函数,最终将一个类对象的引用返回给左边的变量

eg.

析构器(The Destructor)

不同于Java等语言拥有自己的垃圾回收机制,C++需要使用者自己释放创造的对象中使用的内存。在每一个C++类中,都有一个析构函数(Destructor),其命名为~<objectname>

*不同于传统指针(ordinary pointer),C++中的智能指针(smart pointer)会在销毁阶段自动清理占用的内存空间

析构器的调用

C++对象会在以下情况下被销毁

  • 变量会在超出运行范围时被销毁

  • 一个对象成员对象会在它被销毁时销毁

  • 容器中的对象(无论是array或是库容器)会在容器被销毁时销毁

  • 动态分配的指针会在调用delete运算符时被销毁

  • 临时对象会在超出表达式范围时被销毁

由此可见对象的销毁一般是自动的,因此大部分时间我们无需担心对象什么时候销毁,只需要注意销毁时如何释放内存即可

合成的析构器(The Synthesized Destructor)

同理,若不自定义析构函数,C++会自动生成一个合成的析构函数,合成的析构函数不对成员进行任何资源释放

The Rule of Three/Five

对于类的拷贝构造器,拷贝赋值运算符,析构器,我们定义了一些规则以便开发者快速理解自己什么时候需要它们。

需要析构器的类需要拷贝构造器和拷贝赋值运算符

一个类是否需要析构器一般较为明显

eg. 如果一个类中有指针类型成员,它肯定需要析构器对分配的内存进行释放,那么如果这个类没有相应的拷贝构造器,那么进行拷贝过后指针类型成员会被直接复制,因此会有多个对象的指针指向同一块内存,由于这些对象都会进行销毁,delete <pointer> 将会被调用多次,释放已经释放的内存,会使程序报错,并且这个错误是未定义的。

需要拷贝构造器的类需要拷贝赋值运算符,反之同理

比较好理解就不解释了

Using = default

我们可以使用=default显式的告诉编译器对类使用合成的Copy Control成员

合成的function是作为inline function存在的,如果我们不想让这个合成的成员为一个inline function,我们就可以把它设置为=default

避免拷贝的方法(Prevent Copies)

有时我们可能不想让一个类的对象被复制,我们有两种方法可以避免类的复制

定义方法=delete

我们可以显式的定义一个方法=delete,通过定义copy control function=delete,我们就可以避免这个类的复制

eg.

*由于一个类一定会在销毁时调用析构函数,因此我们不能定义类的析构函数=delete

使合成的Copy Control 为deleted

在一些情况下,编译器会自动合成copy control member as deleted

例如将一个函数设为private,这样这个函数在类之外就无法被调用,因此就会被编译器设置为deleted

eg. UE的Noncopyable类型,将拷贝构造器和拷贝复制运算符设置为了private

*在C++ Primer中,作者推荐使用=delete而不是private来实现noncopyable

Reference

C++ Primer, 5th Edition

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值