最近在看《深入探索C++对象模型》,看到默认构造函数,在这里谈一下自己的理解。
之前在看《C++ primer》的时候,看到过:当一个类没有构造函数时,编译器会合成一个默认构造函数。但是这个合成的构造函数做什么呢?其实合成的默认构造函数只是满足编译器的需要,而不能满足我们的需求。例如
class A
{
public:
int x;
};
我想要初始化x(假设想要初始化为0),但是自己没有构造函数,这是编译器会合成一个默认构造函数,当我定义A的对象时,会执行这个默认构造函数,但是这个默认构造函数并不会初始化x。
那么既然合成的默认构造函数不初始化我们自定义的变量(non-static),那么合成的默认构造函数做什么用?
1、如果类的data member是类,且有默认构造函数。那么这个类会合成默认构造函数(前提是它没有其他构造函数),在这个构造函数中,调用data member的默认构造函数。
#include<iostream>
using namespace std;
class Base
{
public:
Base():x(10){};
int x;
};
class Derive:public Base
{
public:
int y;
};
int main()
{
Derive d;
cout<<d.x<<endl<<d.y;
return 0;
}
结果为:
10
-858993460
从y的值也可以看出,默认构造函数没有初始化y。
上面这个例子中类Derived合成了默认构造函数,那么如果我们自己定义了它的构造函数,结果会怎样?
#include<iostream>
using namespace std;
class Base
{
public:
Base():x(10){};
int x;
};
class Derive:public Base
{
public:
Derive(int k):y(k){};
int y;
};
int main()
{
Derive d(5);
cout<<d.x<<endl<<d.y;
return 0;
}
结果为
10
5
这是怎么实现的呢?那么这个类会合成默认构造函数(前提是它没有其他构造函数)。这里有了构造函数,那么就不会合成构造函数了,编译器要做的其实就是把已定义的构造函数“扩展”,在其中插入调用基类构造函数的代码。插入调用基类的代码,在初始化继承类成员变量之前。
2、如果类的data member是类,且有默认构造函数。并且这个类也有默认构造函数。
对于这种情况,就像上面一样,编译器不会再重新合成默认构造函数了,它要做的只是在自己定义的默认构造函数中插入代码,调用基类的默认构造函数。
3、带有虚函数的类
带有虚函数的类包括1、类声明中有虚函数。2类继承基类,在基类中有虚函数。
我们知道虚函数的调用是在运行时确定的,基于动态绑定。而动态绑定的实现是依靠虚函数表(vtbl)和虚函数指针(vptr)。虚函数表是在编译时确定下来的,虚函数指针时放在类对象的起始地址。vptr是在对象构建时确定下来,也就是在构造函数中实现的。
如果有虚函数的类没有构造函数,编译器会合成默认构造函数来做这些事情。
4、虚继承
虚继承是为了防止“菱形继承”,即解决多重继承情况下公共基类的多重拷贝问题。但是当使用了虚继承后,怎么调用公共基类中的变量。
简单来说就是添加了一个虚基类表,在这个表里面记录公共变量的偏移地址,通过这个偏移地址来重新计算公共基类在继承类中的位置。这样就需要一个指向虚基类表的指针放在继承类中,这也就需要构造函数来实现了。