C++派生类构造函数调用规则

派生类构造函数可以自动调用基类的默认构造函数,而无需显式调用。例如在上一节例 2 中,即便将 condingbook 类中的默认构造函数codingbook():book(){lang = none;}语句修改为codingbook(){lang = none;},程序的运行结果依然保持不变,因为派生类的构造函数会自动调用基类的默认构造函数。

派生类构造函数可以自动调用基类的默认构造函数,但是前提是默认构造函数必须存在。通常情况下,默认构造函数系统会自动生成的,但是如果在基类中我们自定义了一个带参数的构造函数,系统将不再自动生成默认构造函数,此时派生类就无法自动调用基类的默认构造函数了,因为基类根本就不存在默认构造函数。

遇到这种情况,有两种解决方案:
  • 在基类中定义一个默认构造函数(不带参数的构造函数),例如上一节中的例2;
  • 派生类中的每一个构造函数都显式的调用基类中的带参构造函数。

【例 1】
class base
{
public:
    base(int a){x = a; y = 0;}
    base(int a, int b){x = a; y = b;}
private:
    int x;
    int y;
};

class derived: public base
{
public:
    derived(){z = 0;}
    derived(int c){z = c;}
private:
    int z;
};
这个例子中,base 基类拥有两个成员变量 x 和 y,同时定义了两个带参数的构造函数。如此一来,base 类就不会自动生成默认构造函数了。derived 派生类中新增了成员变量 z,并且定义了一个带参构造函数和一个默认构造函数。但是如此定义,编译器会提示语法错误,因为派生类的构造函数没有显式调用基类构造函数。

解决这个问题的方法有两种,一个是在 base 基类中定义一个默认构造函数,另外一种方法是 derived 派生类的构造函数显式调用基类的构造函数。当然在设计类的时候推荐使用后者,毕竟构造函数就是为了初始化成员变量的,如果不显式调用基类构造函数,则从基类中继承过来的成员变量将得不到初始化,这一般来说都不是我们所希望看到的。

同时,我们还建议在设计类的时候为每一个类设计一个默认构造函数,毕竟默认构造函数并不会妨碍构造函数的显式调用。通常我们还会遇到这样一种情况,派生类中并未显式定义构造函数,这个时候派生类中只有系统自动生成的默认构造函数,如此一来,如果我们不为基类设计一个默认构造函数,则程序就会编译出错。这种错误很玄妙,如果不小心还真是难以发现。为了避免这种情况的发生,我们建议为每一个类设计一个默认构造函数。

根据以上两点建议,我们对例 1 进行修改,正确代码如下。

【例 2】
#include<iostream>
using namespace std;

class base
{
public:
    base(){x = 0; y = 0;}
    base(int a){x = a; y = 0;}
    base(int a, int b){x = a; y = b;}
private:
    int x;
    int y;
};

class derived: public base
{
public:
    derived():base(){z = 0;}
    derived(int a, int b, int c):base(a,b){z = c;}
private:
    int z;
};

int main()
{
    derived A;
    derived B(1,2,3);
    return 0;
}
这个例子中,我们为基类定义了默认构造函数,并且在派生类中显式地调用基类中的构造函数。

总的来说,在创建派生类对象时,必须显式或隐式地调用基类的某一个构造函数,这一点非常重要。当然被调用的基类的构造函数可以是带参构造函数,也可以是默认构造函数。