1、 一个类是不是一定有构造函数?
C++ Annotated ReferenceManual(ARM):构造函数只在需要的时候才会被编译器产生出来。关键字“在需要的时候”,被谁需要?一种是程序需要的时候,一种是变编译器需要的时候。如果程序有需要(如,完成某些非静态数据成员的初始化),那是程序员的责任,程序员需要自己提供构造函数。如果是编译器需要则才会合成出来默认构造函数。编译器不会替程序员完成任何多余的工作。
2、 编译器什么情况下产生默认构造函数?产生的默认构造函数做些什么?
1、 类A中包含类B对象的数据成员,且类B带有默认构造函数。编译器将会合成默认构造函数用以调用B的默认构造函数完成类B对象的初始化。
2、 类A拥有一个带有默认构造函数的基类B,编译器将会合成默认构造函数用以调用B的默认构造函数。(无论是明确声明或是被合成而得)
3、 类A中包含一个虚函数时,编译器将会合成默认构造函数用以完成对象模型中虚函数表指针的初始化。
4、 类A拥有一个虚基类时,编译器将合成默认构造函数用以安插“允许每一个虚基类的执行期存取操作”的代码。
备注1:如果程序员已提供默认构造函数,则编译器将所需的操作插入该构造函数完成扩展。
备注2:C++书籍里常见的错误
1> 任何class如果没有定义默认构造函数,就会被合成出来一个
2> 编译器合成出来的默认构造函数会明确的设定“class内每一个data member的默认值”。
如你所见,没有一个是真的!
3、 编译器什么情况下产生复制构造函数?
“在必要的时候”,即class不展现出bitwise copy semantics时。
1、类A中包含类B对象的数据成员,且类B带有复制构造函数。编译器将会合成复制构造函数用以调用B的复制构造函数完成类B对象的初始化。
2、类A拥有一个带有复制构造函数的基类B,编译器将会合成复制构造函数用以调用B的复制构造函数。(无论是明确声明或是被合成而得)
3、类A中包含一个虚函数时,编译器将会合成复制构造函数用以完成对象模型中虚函数表指针的重新设定。
4、类A拥有一个虚基类时,编译器将会合成复制构造函数用以完成对virtual base class的特殊处理(发生在derived class初始化base class的情况下,需要存取基类的操作)。
备注:说的更明白一些同默认构造函数产生的情况类似(一致)。
4、 复制构造函数的程序转化。
复制构造函数的调用发生在以下三种情况下:明确的初始化操作、参数的初始化、返回值得初始化。复制构造函数的调用都伴随着程序转化:
1、 明确的初始化操作:
重写每一个定义,其中的初始化操作被剥除;类的复制构造调用操作被安插进去。
X x0;
void foo_bar(){
X x1(x0);
//...
}
转化后
void foo_bar(){
X x1; //定义重写,初始化操作被剥除
x1.X::X(x0);
}
2、 参数的初始化
已知的函数定义:void foo(X x0);
X xx;
foo(xx);
转化后
X __tmp0;
__tmp0.X::X(xx);
foo(__tmp0);
3、 返回值的初始化
添加额外参数,类型是class object的一个reference,用来防治被拷贝构建而得的返回值;在return之前安插一个复制构造的调用操作。
X bar(){
X xx;
//处理xx...
return xx;
}
转化后
void bar(X& __result){
X xx; //注释:看到没,所谓的NRV优化就是可以将这个变量优化掉,直接处理__result;
xx.X::X();
//处理xx...
__result.X::X(xx);
return ;
}
所以 X xx=bar();将被转化为 X xx; bar(xx);
5、成员初始化列表(member initializationlist)
以下四种情况必须使用成员初始化列:
1、 当初始化一个引用类型的数据成员时;
2、 当初始化一个const数据成员时;
3、 当调用基类的带参构造函数时;
4、 当调object类型的数据成员的带参构造函数时;
前两者因为要求定义时初始化,所以必须明确的在初始化队列中给它们提供初值。后两者因为需要显式的调用它们的带参构造函数而非默认构造函数在定义时初始化它们。
备注:说清楚点就是所有需要在定义时初始化的都需要使用初始化列表。