zoukankan      html  css  js  c++  java
  • 复制控制(学习<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、注意:即便自定义了自己的析构函数,编译器在运行自定义析构函数之后,还会运行合成析构函数。

     

     

     

     

     

     

          

     

     

     

     

     

     

     

     

  • 相关阅读:
    druid的配置
    js学习(五)-全局函数和类内部函数区别
    js学习(四)- prototype原型对象
    js学习(三)-使用大括号({ })语法创建无类型对象
    js学习(一)-动态添加、修改、删除对象的属性和方法
    js学习(一)-对象和函数概念
    js关于函数和对象的概念
    js传递数组到后台
    ubantu一些资料
    fiddler的编程文章
  • 原文地址:https://www.cnblogs.com/gdutbean/p/2242254.html
Copyright © 2011-2022 走看看