类的拷贝与控制(1)拷贝控制操作

来源:互联网 发布:单片机ds18b20工作原理 编辑:程序博客网 时间:2024/06/05 10:24

我们在制定类的对象拷贝、移动、赋值和销毁时,一般会使用下面五中特殊的成员函数来定义:

  • 拷贝构造函数 (copy constructor)
  • 拷贝赋值运算符 (copy-assignment perator)
  • 移动构造函数 (move constructor)
  • 移动赋值运算符(move-assignment operator)
  • 析构函数(destructor)

以上的这些操作被称为:拷贝控制操作(copy control)

若一个类没有自定义这写拷贝控制成员编译器会自动为它定义所缺失的操作,但是对于有些类而言,使用编译器所定义的默认操作常常会导致灾难!!!

拷贝构造函数

认识: 第一个参数是自身类类型的引用,切任何额外参数都有默认值构造函数

【问】:为何第一个参数必须是引用
【原因】: 函数调用过程中,具有非引用类型的参数要进行拷贝初始化,若拷贝构造函数不是引用类型的参数,则会调用该类类型的拷贝构造函数进行拷贝初始化,则会无限调用下去。

合成拷贝构造函数

若我们没有为一个类定义拷贝构函数,无论我们是否定义了其他构造函数(与合成默认构造函数不同点),只要不存在自定义拷贝构造函数,编译器会为我们定义一个,称为合成拷贝构造函数。

合成拷贝构造函数的作用:
(1)阻止我们拷贝该类类型的对象;
(2)将其参数的成员逐个拷贝到正在创建的对象中。

合成拷贝函数的拷贝过程:
(1)类类型的成员将调用其拷贝构造函数;
(2)内置类型的成员则直接拷贝;
(3)逐一拷贝数组类型的成员。
可以说,每个成员的类型决定了它们的拷贝方式。

直接初始化与拷贝初始化:

string dots(10,',');    //直接初始化string s(dots);         //直接初始化string s2 = s;                          //拷贝初始化string null_book = "9-9999-999999-9";   //拷贝初始化string nines = string(100,'9');         //拷贝初始化
  • 直接初始化:要求编译器使用普通的函数匹配来选择与我们提供参数最匹配的构造函数;
  • 拷贝初始化:要求编译器将右侧运算对象拷贝到正在创建的对象中,还会进行类型转换,通常使用拷贝构造函数来完成。(例外,优先使用移动构造函数

拷贝初始化另外的情况:

  • 将一个对象作为实参传递给一个非引用的类型的形参;
  • 从一个返回类型为非引用类型的函数返回一个对象;
  • 花括号列表初始化一个数组中的元素或一个聚合类(struct)中的成员。

一个explicit修饰的构造函数,不能隐式地被调用。

拷贝赋值运算符

赋值运算符为一个成员函数;通过对赋值运算符的重载控制其对象如何赋值。

合成拷贝赋值运算:类似于合成拷贝构造函数,可以用来禁止该类型对象的赋值;除外一般情况为将右侧运算对象的每个非static成员赋予左侧运算对象的对应成员。

析构函数

【认识】析构函数执行与构造函数相反的操作:
(1)构造函数初始化对象的非static数据成员,附带做一些其他工作;
(2)析构函数释放对象所使用的资源,并销毁对象的非static数据成员。

调用析构函数的情况:一个对象被销毁时都会调用析构函数。
(1)变量离开作用域时被销毁;
(2)当一个对象被销毁时,其成员被销毁;
(3)容器被销毁时;
(4)动态分配的对象,当对指向它的指针应用delete运算符时被销毁;
(5)对于临时对象,当创建它的完整表达式结束时被销毁。

合成析构函数:一个类未自定义自己的析构函数时,编译器会自动为其合成一个析构函数-合成析构函数。其作用被用来阻止该类型的对象呗销毁。除此之外,合成析构函数的函数体为空!

一个类需要自定义拷贝控制操作的情况:

(1)三个操作通常被看做一个整体,一般应该全部定义
(2)需要析构函数的类也需要拷贝和赋值操作
(3)需要拷贝操作的类也需要赋值操作

阻止拷贝操作

【认识】对于大多数的类,都需要拷贝构造函数拷贝赋值运算符,但还有一些类,并不需要这些拷贝操作,甚至拷贝操作是被禁止的。因此我们在定义类中,可以采用某种机制阻止拷贝或赋值。

机制一:新标准中delete关键字使用

struct NoCopy{    NoCopy() = default;    NoCopy(const NoCopy&) = delete;//阻止拷贝    NoCopy& operator=(const NoCopy&)  =delete;//阻止赋值    ~NoCopy()= default;}

使用delete注意:不能对析构函数使用delete,否则会无法销毁次类型的对象了。

机制二:private关键字修饰拷贝控制

例子:

class PrivateCopy{public:     PrivateCopy() = default;    ~PrivateCopy();private:    PrivateCopy(const PrivateCopy&);    PrivateCopy& operator= (const PrivateCopy&);//声明,但不定义,防止友元和成员函数进行拷贝;}

移动构造函数(待补充)

移动赋值运算符(待补充)

0 0