zoukankan      html  css  js  c++  java
  • 关于拷贝构造

    有3种情况,可以将一个object的内容作为另外个object的初值:

    1. 明确的拷贝:X c_x2; X c_x = c_x2;

    2. 函数参数:void foo(X xx);

    3. 函数返回值:X foo() { X xx; return xx;};

    假如类的设计者定义了一个拷贝构造,比如:

    X::X( const X &x);

    Y::Y( const Y &y, int = 0);

    那么在大部分情况下,当一个类对象以另一个同类实体作为初值时,上述的构造会被调用。

    位逐次拷贝:

       1:  //...
       2:  Word noun("book");
       3:   
       4:  void foo()
       5:  {
       6:       Word verb  = noun;
       7:  }

    很明显,verb是根据noun来进行初始化。但如果没见过Word的声明,无法确定是否显式的声明了拷贝构造?如果没有声明,那么是否编译器会自动合成实体调用呢?

       1:  //声明方式1:语意拷贝
       2:  class Word
       3:  {
       4:  public:
       5:       Word(const char*);
       6:       ~Word(){delete []str;};
       7:  private:
       8:       int cnt;
       9:       char *str;
      10:  }

    在这样的拷贝声明下,当Word verb = noun时,verb和noun的str指针都指向相同的字符串“book”。因为Word(const char*)时,拷贝的并不是一个字符串内容,而是一个指向字符串“book”的指针(该指针指向字符串的地址)。这是灾难性的,一旦其中一个对象被销毁,另外个对象str指针便指向了一个危险的地址。

    如果采用另外种声明方式:

       1:  class Word
       2:  {
       3:  public:
       4:       Word(const Word &word);
       5:       ~Word();
       6:  private:
       7:       int cnt;
       8:       char *str;
       9:  }

    回忆下2个扩张操作:

    1. 增加一个“虚函数表”(vtbl),内含有每一个有作用的虚函数地址;

    2. 将一个指向虚函数表的指针(vptr),安插在每一个类对象内;

    所以这里的可怕点在于,vptr是否被正确设置好初值(如果没有指向正确的vtbl,那么后果会很严重)。

     1 class ZooAnimal
     2 {
     3 public:
     4      ZooAnimal();
     5      virtual ~ZooAnimal();
     6      
     7      virtual void animate();
     8      virtual void draw();
     9 private:
    10      // 各种数据
    11 }
    12 
    13 class Bear : public ZooAnimal
    14 {
    15 public:
    16      Bear();
    17      void animate();
    18      void draw();
    19      virtual dance();
    20 private:
    21      // 各种数据
    22 }

    然后这样:

    Bear weini;
    
    Bear huangseweini = weini;

    这里的Bear类对象以另外一个Bear对象作为初值,都是依靠位拷贝来完成。

    首先weini对象,先由Bear默认构造来完成初始化,这里的vptr(weini)被设定指向Bear类对象的虚表(这里由编译器安插代码来完成)。所以,把weini的vptr指针拷贝给huangseweini的vptr是安全的。

    image

    当一个基类对象以其子类作为对象内容进行初始化时,势必发生切割行为。此时vptr的复制操作也必须保证安全。

       1:  void draw(const ZooAnimal &zoey){ zoey.draw();};
       2:  void foo() {
       3:       ZooAnimal franny = weini;
       4:       draw(weini);     // 这里调用的是Bear::draw()的接口 
       5:       draw(franny);    // 这里调用的仍然是ZooAnimal::draw()的接口
       6:  }
    image 
    也就是说,合成出来的ZooAnimal拷贝构造会明确的设定对象的vptr指向ZooAnimal的虚表,而不是直接从右手边的对象中将其vptr拷贝过来。
     
  • 相关阅读:
    python与常用模块pandas,numpy,matplotlib等库学习笔记-2019.02.07更新
    C++异常处理相关用法及底层机制
    C++ regex库常用函数及实例
    leetcode-2-两数相加(链表)
    leetcode-1-两数之和(三种方法)
    中序遍历(递归+迭代)
    C++实现四则运算器(带括号)
    C++实现四则运算器(无括号)
    Visual Studio2019 基于WSL的Linux C++开发
    Visual Studio 2019 基于Linux平台的C++开发
  • 原文地址:https://www.cnblogs.com/davidsguo008/p/3652773.html
Copyright © 2011-2022 走看看