什么时候编译器会合成一个默认构造函数?
答案:当编译器需要它的时候!(如果程序需要一个默认构造函数,那是程序员的责任,应该由程序员自己定义)并且被合成出来的构造函数只执行编译器所需的行动。
注意:1.并非所有没有构造函数的类在编译时都会生成默认构造函数(但一些大学老师为了讲课方便就这么讲的)。
2.只有类中没有显示声明任何构造函数时,编译器才可能会为类生成默认构造函数
例:
//定义的Fool类
class Foo{
public:
int val;
Foo *pnext;
}
int main(){
Foo bar;
cout<<bar.val<<" "<<bar.pnext<<endl;
return 0;
}
错误思想:在main函数中定义bar对象时,编译器会生成一个默认构造函数去初始化bar对象。
正确思维:编译器不会合成一个默认构造函数,因为编译器不需要构造函数来做任何事情。对于类的数据成员的初始化,那是程序员的事,应该程序员自己定义构造函数去做。
有以下4种情况,编译器认为有必要合成默认构造函数
1.带有 Default Constructor 的Member Class Object
情况1: 如果一个class A没有任何构造函数,并且它内含一个类的成数据成员对象 b,而b的类Class B 有默认构造函数,那么编译器就会在定义类A的一个对象 object a 时合成默认构造函数。(因为在构造对象a 时需要调用类B 的默认构造函数来处理b,因此需要为a 合成一个默认构造函数)
例:
class B{
public:
B();//默认构造函数
B(int);//带参数的构造函数
//其余代码
..........
};
class A{
public:
B b;
char *str;
};
int main(){
A a;
if(a.str){
.....
}
return 0;
}
上面代码中 类A 在构造a时,编译器会生成会生成一个默认构造函数,并且被合成出来的构造函数内包含必要的代码,能调用class B 的默认构造函数来处理 类A 中的数据成员b.注意:被合成出来的构造函数只满足编译器的需求,而不是程序的需要,他不会去初始化 成员str
合成出的构造函数伪代码如下:
inline A::A(){
b.B::B()
}
情况2:在上述例子中,假如类A含有一个默认构造函数,类A函数如下:
class A{
public:
B b;
char *str;
B(){
str=nullptr;
}
};
此时,因为类A显示定义了一个默认构造函数,因此编译器不会再为类A合成默认构造函数,但是编译器会悄悄的在类B的构造函数体开始位置(在程序员写的代码前)插入一些代码用于调用类B的构造函数。此题中,代码安插在 str=nullptr;之前
2.带有 Default Constructor 的Base Class
如果一个没有任何构造函数的类派生自一个带有默认构造函数的基类,那么这个派生类将会被合成出来,用以调用它父类的默认构造函数。调用顺序为声明顺序
3.带有一个Virtual Function 的Class
class声明或继承了一个 virtual function
因为类中存在虚函数,因此该类将含有一个虚函数表以供多态时使用,于是在生成类的对象时,对象中必须有一个能指向虚函数表的虚函数指针,类必须合成一个默认构造函数用以初始化该虚函数指针
4.带有一个 Virtual Base Class的Class
例如砖石型继承关系,为了解决重复继承,因此使用了虚继承
类的继承中存在虚继承关系,因此虚继承的类会共享同一个虚继承父类的对象,这种共享是用指针指向完成的,为了初始化该指针,类也必须合成默认初始化函数
以上总结如有错误请指正,大家共同进步,谢谢