复制控制(学习<C++Primer>)

复制控制

一、复制控制

类能控制复制,赋值,撤销该类的对象时的动作,分别通过下面的成员函数:

1. 复制构造函数:具有单个形参,该形参是对该类类型的引用(通常用 const修饰);

2. 赋值操作符

3. 析构函数:不管有没显示定义,编译器都自动执行类中非static数据成员的析构函数

这三个函数就成为复制控制。

 

二.为什么要研究复制控制

如果没有显示地定义复制构造函数和赋值操作符,编译器会为我们定义。

但是编译器合成的复制控制函数只做必需的工作,某些类如果依赖于默认的定义会导致错误,例如类具有指针成员。

 

难点: 识别何时需要覆盖默认版本,定义自己的复制构造函数

 

所以有时候要自定义复制构造函数,定义复制构造函数跟构造函数是一样的,难点是能认识到需要定义复制构造函数。

(具体请看第五点:”什么时候要自己的复制构造函数”)

                  复制控制函数

三.复制构造函数会几时用到?

首先:区分构造函数和复制构造函数

 

1.根据另一个同类型的对象显式或者隐式地初始化一个对象

  区别下面:

(1)下面的构造函数和复制构造函数仅在低级别优化上存在差异

String str3(10,"a"); // 调用构造函数

String str4 ; // 调用构造函数

 

String str1= "hello";  // 调用复制构造函数

String str2 = string(); // 调用复制构造函数

(2)对于不支持复制的类型,或者使用非explicit构造函数的时候,它们有本质区别:

 

            Ifstream file1("filename");  //OK,调用构造函数

            Ifstream file2 = "filename" ; //ERROR, 不能复制IO类型的对象

 //(复制构造函数是private 的)(原因见第七项)

            

           Sales_item item = string("hello"); //取决于构造函数是不是explicit,如果构造函数 是显式的,则初始化失败,如果构造函数是隐式的,则初始化成功。

 

2.一个对象作为实参传给一个函数,或者作为函数的返回值

 

  例如: string fun(const stirng& A, const string B);

返回值和 第二个参数B隐式地调用复制构造函数;第一个参数A,const引用,不能复制。

 

3.初始化顺序容器中的元素

  当用表示容量的单个形参来初始化容器的时候,用到了默认构造函数和复制构造函数。

 

 Vector<string> svec(5);

先用string默认构造函数创建一个临时值来初始化svec,然后是使用复制构造函数将临时值复制到svec的每一个元素。

 

4.根据“元素初始化列表"初始化数组元素

(1)如果没有为类类型数组提供元素初始化式,是调用默认构造函数初始化每个元素,

     Sale_item  ClassArray[];

 

(2)如果用花括号扩展的数组初始化列表来提供显式元素初始化,则是用复制构造函数

根据指定值创建适当类型的元素,然后用复制构造函数将该值复制到相应的元素。   

 

Sale_item ClassArray2[] = {    string("ab"),  //直接指定一个值,调用单实参构造函数

string("cd"),

Sale_item(), // 使用完整的构造函数语法(0或者多个实参)

                       };

 

备注:可以直接指定一个值,调用元素类型的单实参构造函数,如前两个元素的初始化;

如果不指定实参或者指定多个实参,就需要使用完整的构造函数语法,如最后一个元素的初始化

  

 

四.编译器合成的复制构造函数

  前面提到,如果我们没有定义,编译器会默认合成一个复制构造函数,该函数会对该对象的每一个非static成员依次复制到正创建的对象。

其中,不同类型的复制如下:

1.直接复制内置类型成员的值(不是指针)

2.类型成员使用该类的复制构造函数

3.数组复制数组的每一个元素

等价于: 一个构造函数,每个数据成员在构造函数初始化列表中进行初始化;

例如 有三个数据成员的类的合成构造函数:

Sales_item::Sales_item(const Sales_item &orig)

        A(orig.A),

        B(orig.B),

        C(orig.C)

{//Empty}

 

       

五.什么时候要自己定义复制构造函数(重点)

   有些类必须对复制对象时发生的事情进行控制,例如

1、类中有数据成员是指针或者有成员在构造函数中分配其他资源

2、在创建对象时必须做一些特定工作

以上的情况都必须定义复制构造函数。

 

六、怎么定义复制构造函数(同构造函数一样)

 

 同类同名,没有返回值,可以使用构造函数初始化列表初始化新创建对象的成员,可以在函数体中任何其他必要的工作。

 

七、如何禁止复制

 

   例如:iostream类就不允许复制

     1.如果不自定义复制构造函数,编译器也会自动合成一个,无法禁止复制

     2.为了防止复制,类必须显式地声明其复制构造函数为 private(其友元和成员依然可以复制)

     3.如果要连友元和成员的复制也禁止,就可以声明一个private的复制构造函数但不对它进行定义。(如果复制类对象会提示编译错误,如果成员和友元尝试复制就会导致链接错误)

 

八、赋值操作符

1.如果没有定义自己的赋值操作符,编译器也会自动合成一个(类似于合成的复制构造函数,进行各个成员的赋值)

2.如果类自定义自己的复制操作符,那么一般类也需要自定义赋值操作符(具体的请看后面的例子)

 

九、析构函数

 1、 析构函数通常用于释放在构造函数或者在对象生命期内获取的资源。析构函数可以执行任意操作,一般是在类对象使用完毕之后要执行的动作。

  三法则: 如果类需要析构函数,则它也需要复制操作符和复制构造函数。

 

2、合成的析构函数按创建的逆序撤销每个非static 成员。(并不删除指针成员所指向的对象),无论有没创建自定义函数,编译器都会合成析构函数

 

3、注意:即便自定义了自己的析构函数,编译器在运行自定义析构函数之后,还会运行合成析构函数。

 

 

 

 

 

 

      

 

 

 

 

 

 

 

 

原文地址:https://www.cnblogs.com/gdutbean/p/2242254.html