第二章构造函数语义学——关于构造函数和复制构造函数
以下代码体现了书中所说的如下几个知识:
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。
所以最后的输出是这样的。
编译器真的辛苦的说啊。