关于拷贝构造

有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拷贝过来。
 
原文地址:https://www.cnblogs.com/davidsguo008/p/3652773.html