最近在看《深入探索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、虚继承

虚继承是为了防止“菱形继承”,即解决多重继承情况下公共基类的多重拷贝问题。但是当使用了虚继承后,怎么调用公共基类中的变量。

简单来说就是添加了一个虚基类表,在这个表里面记录公共变量的偏移地址,通过这个偏移地址来重新计算公共基类在继承类中的位置。这样就需要一个指向虚基类表的指针放在继承类中,这也就需要构造函数来实现了。