Inside the C++ Model第二讲之 Copy Constructor的构造操作

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010772289/article/details/75807956

Default Memberwise Initialization

如果class没有提供一个explicit copy constructor又当如何呢?当class object以”同一个class的另一个object”作为初值,其内部是以default memberwise initialization来完成的.也就是把每一个内建的或派生的data member(例如一个指针或一个数组),从某一个object拷贝到另一个object中.不过它并不会拷贝其中的member class object,而是以递归的方式实施memberwise initialization.
其实说白了,上一段话的意思就是,如果一个类没有定义拷贝构造函数,那么在拷贝类的对象时,将它的数据成员直接拷贝到另一个对象中(这个过程称为Bitwise Copy Semantics,也就是位逐次拷贝,通常我们说的浅拷贝就是这种拷贝),不过它不会直接拷贝member class object,而是以memberwise initialization(这个就对应着深拷贝)的方式拷贝的.直接拿来英文版的例子,感觉中文翻译太拗口了.

class String {
public:
// ... no explicit copy constructor
private:
char *str;
int len;
};

The default memberwise initialization of one String object with another, such as

String noun( "book" );
String verb = noun;

is accomplished as if each of its members was individually initialized in turn:

// semantic equivalent of memberwise initialization
verb.str = noun.str;
verb.len = noun.len;

If a String object is declared as a member of another class, such as the following:

class Word {
public:
// ...no explicit copy constructor
private:
int _occurs;
String _word;
};

then the default memberwise initialization of one Word object with another copies the value of its built-in member _occurs and then recursively applies memberwise initialization to its class String member object _word.

Bitwise Copy Semantics(位逐次拷贝)

参考 http://www.cnblogs.com/cyttina/archive/2012/11/21/2781453.html
关于这个问题,第一遍看的时候比较晕,不知道这个什么 bitwise copy是个什么东西,其实很简单,就是 位逐次拷贝(我靠,一句好犀利的废话)。额,具体来说呢,就是对 源类中的成员变量 中的每一位 都逐次 复制到 目标类中。具体的内容接着看。
首先让我们看看这个概念是怎么出来的。书中第50页的时候说到:
Default constructors 和 copy constructors 在 必要的时候 才由编译器产生出来
这个句子中的“必要”意指当class不展现bitwise copy semantics时。

这段话的意思呢,应该是这么个意思: 如果class中出现了bitwise copy semantics的时候,default constructors和copy constructors 编译器就不会为我们产生出来(又像一句废话)。那么没有default constructors和copy constructors的时候,我们的类怎么产生呢。这时候,就是通过 bitwise copy 来搞定了,也就是 将 源类中的成员变量中的每一位都逐次复制到 目标类 中,这样我们的类就构造出来了。(当然,这个是我的理解,这个 是否是 源类中的成员变量,还有待于证实)。
这时候,这种bitwise copy构造的类 就会存在一个问题,就是对于指针变量来说,源类中的指针变量保存的是开辟的空间,而目标类中的的指针变量,是通过逐位复制的方式得到的,这样,目标类中的指针变量保存的地址和源类的是一样的。当源类释放空间之后,目标类中的指针变量指的是一堆无意义的空间,这样,当目标类中的指针变量再释放空间的时候,就会报错。这段也就是P52注释所说的内容。
扯了这么多,还是看代码吧,把书中的例子完全实现了下。代码如下:

class Word
{public:
    Word(const char* s){
        str = new char[strlen(s)+1]; // 开辟空间        strcpy(str,s);
        cnt = strlen(s)+1;
    }
    ~Word(){
        delete[] str; // 释放空间    }
public:
    int cnt;
    char *str;
};

int main(int argc, _TCHAR* argv[])
{
    Word noun("book");//调用构造函数
    Word verb = noun;// 不会产生copy constructors
    cout<<verb.str<<endl;
    cout<<verb.cnt<<endl;

    cout<<"noun.str address = "<<(unsigned long)(noun.str)<<endl;
    cout<<"verb.str address = "<<(unsigned long)(verb.str)<<endl;
    return 0;
}

结果如下:

book
5
noun.str address = 3243208
verb.str address = 3243208

可以看到,完全复制了过去,而且指针的地址是一样的,只是最后会报错,也就是之前讨论过的,释放空间的问题。
到这里关于bitwise copy就这么多了,关于什么时候会调用bitwise copy进行复制呢,见书53页。

不要Bitwise Copy Semantics
什么时候一个class不展现出”bitwise copy semantics”呢?有4种情况:
1.当class内含一个member object而后者的class声明有一个copy constructor时
2.当class继承自一个base class而后者存在一个copy constructor时(不论是被显式声明或被合成得到)
3.当class声明了一个或多个virtual functions时.
4.当class派生自一个继承串链,其中有一个或多个virtual base classes时.

重新设定Virtual Table的指针
在第3种情况下,因为class包含virtual functions,需要做扩张操作:
■增加一个virtual function table (vtbl),内含每一个virtual function的地址.
■一个指向virtual function table的指针(vptr),安插在每一个class object内.
当编译器导入一个vptr到class之中时,该class就不再展现bitwise semantics了.现在,编译器需要合成出一个copy constructor使得vptr能够适当的初始化.举个例子,定义两个classes, ZooAnimal和Bear.

class ZooAnimal {
  public:
    ZooAnimal();
    virtual ~ZooAnimal();
    virtual void animate();
    virtual void draw();
    // ...
  private:
    // data necessary for ZooAnimal's
    // version of animate() and draw()
};
class Bear : public ZooAnimal {
  public:
    Bear();
    void animate();
    void draw();
    virtual void dance();
    // ...
private:
    // data necessary for Bear's version
    // of animate(), draw(), and dance()
};

如果直接拷贝一个ZooAnimal的对象到另一个ZooAnimal的对象中,或者拷贝一个Bear对象到另一个Bear对象中,那么直接依靠”bitwise copy semantics”(位逐次拷贝)就可以了.举个例子,
Bear yogi;
Bear winnie=yogi;
yogi用Bear的默认构造函数初始化,在构造函数中,yogi的vptr被设定指向Bear类的virtual table(这是靠编译器安插的代码完成的).因此,通过位逐次拷贝,将yogi的vptr值直接拷贝给winnie是安全的.
但是如果将yogi直接通过”bitwise copy”拷贝给ZooAnimal对象则是非常危险的.例如,ZooAnimal franny=yogi; franny的vptr不可以被设定为指向Bear class的virtual table(但如果yogi被直接bitwise copy的话,就会导致此结果).
这里写图片描述

处理Virtual Base Class Subject

virtual base class的存在需要特别处理。一个class object 如果以另一个 virtual base class subobject那么也会使“bitwise copy semantics”失效。
每一个编译器对于虚拟继承的支持承诺,都是表示必须让“derived class object 中的virtual base class subobject 位置”在执行期就准备妥当,维护“位置的完整性”是编译器的责任。Bitwise copy semantics 可能会破坏这个位置,所以编译器必须自己合成出copy constructor。
例如,
这里写图片描述
这里写图片描述
不能将RedPanda对象直接bitwise copy给Raccoon对象.为了完成正确的littel_critter初值设定,编译器必须合成一个copy constructor,安插一些代码以设定virtual base class pointer/offset的初值.

猜你喜欢

转载自blog.csdn.net/u010772289/article/details/75807956