第二章构造函数语义学——关于构造函数和复制构造函数

以下代码体现了书中所说的如下几个知识:

1. 如果一个class没有任何constructor,但它含有一个member object, 而后者有default constructor,编译器需要为该class合成出一个default constructor。(P41)

2. 如果 类中的default constructor已经被显示的定义出来,编译器没办法合成第二个,编译器会采取什么行动呢。编译器会扩张已存在的constructors, 在其中安插一些代码,使得 user code 被执行前,先调用必要的default constructors。(P42)

3. 当class内含一个menber object 而后者的class声明中有一个copy constructor时,不会出现bitwise copy semantics,编译器必须合成一个copy constructor,将members的copy  constructors调用操作,安插到被合成的copy constructor。(P53)

4. 合成复制操作符(synthesized assignment operator)会执行逐个成员复制(memberwise assignment)。(C++ primer P411)。

所用的代码如下:

class String// 注意这个String不是容器类的那个,那个是string
{
public:
    String(){
        cout<<"String()"<<endl;
        len = 5;
    }

    String(const String& o){
        cout<<"String(const String& o)"<<endl;
        len = 10;
    }

    int len;

};

class Word
{
public:
    Word(){  
        cout<<"Word()"<<endl;
    }
    Word(const String& o){
        cout<<str.len<<endl;////注意这里的输出
        cout<<"Word(const String&)"<<endl;
        str = o;
    }
public:
    int cnt;
    String str;
};

int main(int argc, char* argv[])  
{  

    String o;//调用String的默认构造函数
    o.len = 20;
    cout<<endl;
    Word w = o;//调用复制构造函数进行构造
    cout<<endl;
    w.cnt = 100;
    Word w2= w; // 调用复制构造函数,因为没有对应了,则编译器会帮我们产生一个
    cout<<endl;
    cout<<"w.str.len = "<<w.str.len<<endl;
    cout<<"w2.str.len = "<<w2.str.len<<endl;
    cout<<"w2.cnt = "<<w2.cnt<<endl;
    return 0;
}  

输出的结果:

String()

String()
5
Word(const String&)

String(const String& o)

w.str.len = 20
w2.str.len = 10
w2.cnt = 100


根据main中的内容以及结果进行分析:

String o;
//定义个String类的对象o,这里因为String显示定义了构造函数,所以会调用String::String(),结果就是输出String(),以及将 o.len 赋值为5

o.len = 20;// 没啥可说的,将o.len从5赋值为20

Word w = o;
// 这里看到Word中有一个构造函数Word(const String& o) 则调用该函数,但是根据我们的第一个和第二个知识点,编译器看到类中有一个成员变量中包含了构造函数,那么编译器会在调用该函数之前,插入代码调用 该成员变量的构造函数,也就是说,在cout<<str.len<<endl之前,编译器会插入一段代码调用str的构造函数,这样也就会将str.len赋值为5。这样在调用构造函数的时候,会输出String(),在执行cout<<str.len<<endl的时候,会输出5,之后执行cout<<"Word(const String&)"<<endl;输出Word(const String&),下一句代码为str=o,这里因为没有重载运算符=,编译器会为我们合成复制操作符,它会执行逐个成员赋值(C++ primer P412)。这里会将str.len赋值为20

//这里这个函数成为 构造函数,还是称为复制构造函数,还有点问题。C++ primer P406中对于复制构造函数的定义是:只有单个形参,而且该形参是对本类类型对象的引用。在《深度》P48页的Copy Constructor的构造操作中,将等号作为了一种显示的调用方式,而且用的是如下的描述: 以一个object的内容作为另一个class object的初值。

w.cnt = 100

Word w2 = w;
// 这里因为没有定义对应的复制构造函数,所以编译器会帮我们定义一个,并且进行memberwise initialization,先用w.cnt初始化w2.cnt,这样w2.cnt就变成了100,在用w.str初始化w2.str,这里会调用String的复制构造函数,也就是会输出Word(const String&),这样就完成了构造。所以w2.str.len是在String的复制构造函数中进行赋值的,也就是10

// 这里如果先构造出w2,在通过w2=w,进行复制的话,w2.str.len就会等于w.str.len

之后输出。

    cout<<"w.str.len = "<<w.str.len<<endl;
    cout<<"w2.str.len = "<<w2.str.len<<endl;
    cout<<"w2.cnt = "<<w2.cnt<<endl;

w.str.len 是在Word(const String& o)中先被赋值为5,然后在通过 w.str=o,赋值成了o.len也就是20

w2.str.len 是通过编译器生成的 复制构造函数中,调用了String的显示复制构造函数中,进行赋值的,赋值为10

w2.cnt是通过编译器生成的 复制构造函数中进行了赋值,赋值为100。

所以最后的输出是这样的。

 


编译器真的辛苦的说啊。

 

 

 

posted @ 2012-11-21 21:27  三更_雨  阅读(317)  评论(0编辑  收藏  举报