Loading

欢乐C++ —— 12. 类的六种特殊函数

简述

当定义一个类时,我们会显式或隐式地指定此类型的对象构造,拷贝,移动,赋值,和销毁操作。

其中,拷贝构造和移动构造决定了当用同类型的另一个对象初始化新对象时做了什么;拷贝赋值和移动赋值运算符决定了当将一个对象赋值给另一个同类型对象时做了什么。

当一个类没有定义这些操作时,某些情况下编译器会默认为该类定义这些操作(共6种)。不过,由编译器定义的操作常常不是我们想要的,并且有些情况下,编译器并不会为我们默认定义这些操作。

这些函数都有两种额外声明:

  • = default 默认编译器生成,使用默认生成版本。 只能对下面六种成员函数使用。
  • = delete 默认删除函数,禁止使用该函数。可以对所有成员函数使用(包括自定义函数)

注意通常情况下,swap 会拷贝交换,所以我们自定义的类为了避免无意义的拷贝,提高交换效率,在必要的情况下自定义swap操作。

对赋值运算符来说,使用 copy and swap 可以进一步减少代码冗余,避免额外的异常检查。

首先是常见的四种

1. 构造函数

构造函数三个任务:创建对象,初始化对象,进行类型转化。最后一点我们会在运算符重载中类型转化运算符重载有提及。

构造函数所做的工作就是初始化对象的数据成员,也就是初始化一个对象的状态。

构造函数都不能声明为const ,因为当我们创建一个const对象时,意味着只有构造函数初始化列表执行完后,对象才能真正取得const 属性。

构造函数不能直接调用,在代码中看起来像是主动调用构造函数的行为,实际上是显式生成了一个临时对象,然后为该临时对象调用构造函数。

构造函数加 explicit 可以限制编译器隐式调用该构造函数。

没有在初始值列表初始化的成员会被默认初始化。

2. 析构函数

无论何时,只要一个对象被销毁,就会自动调用析构函数。析构函数决定了在销毁一个对象前要干什么。

析构函数所做的工作就是释放对象所分配的外部资源,而不是释放对象本身所占的内存空间。对象本身,实际上是在析构函数体执行完后的隐含的析构阶段进行销毁。

3. 赋值运算

要防止自赋值。

决定了对象间赋值时所干的工作。

4. 拷贝构造函数

如果构造函数的第一个参数是自身类类型的引用,且任何额外参数都有默认值,则此构造函数是拷贝构造函数。拷贝构造函数决定了当用一个对象初始化另一个对象时要做什么。

编译器默认生成的拷贝构造函数会为我们逐个拷贝非静态成员变量。所以,当类内有动态分配的内存时,会发生浅拷贝的问题。此时我们就有必要自定义拷贝构造函数。

通常不应该是 explicit ,因为多种场景下经常隐式调用拷贝构造函数。一般而言,以下的场景会发生拷贝构造:

  1. 将一个对象作为实参传递给一个非引用类型的形参。
  2. 函数的返回非引用对象
  3. 列表初始化
  4. 某些容器操作 ,例如vector.push_back()

为什么参数要是常引用类型?如果参数不是引用,就会无限制的递归调用下去。如果不是常引用,就不能拷贝常属性的对象和临时对象。

其次是两种移动函数

移动函数,说起来就是改善了 当类中有分配的内存时用右值作为实参下 拷贝构造 和 赋值 的效率。

右值是相对于左值这个概念,左值是有名字,有内存,声明周期一样的变量。而右值就是函数的返回的局部量或临时量。

举个例子,当类中有new 分配的内存时,此时用该类的一个A对象构造新的对象B,拷贝构造所做的就是给B拷贝一份和 A中new的内存 大小,内容相同的内存。而如果A是一个右值,则会把它new 的内存直接转让给B。

image-20200118112158707

移动函数的出现改善了容器两个方面:

  1. 使得使用右值作为实参时对象的拷贝赋值效率更高。
  2. 使得容器可以存储不可拷贝的对象,例如(iostream)。

当没有定义这两种移动函数时,调用时则会调用相应拷贝版本。

不抛出异常的两种移动函数都应该额外声明noexcept。

可以使用 中的std::move() 函数将一个左值转化为右值。在使用move函数后,要保证不再使用移动后的源对象的值。并且在移动函数中要注意在移动后,保证右值是可析构的状态(例如把右值的指针域置空)。

5. 移动构造函数
6. 移动赋值运算符

需要检查自赋值,防止该右值是由当前对象经move生成的,避免自赋值。而使用copy and swap 就没有这个必要。

总结

image-20200510170310277

当希望阻止使用某个函数,最好是delete 此函数,虽然将该函数设置成私有也是可以,但当有友元类型时,后者做法就不合适。

posted @ 2020-04-22 11:21  沉云  阅读(559)  评论(0编辑  收藏  举报