在构造函数的参数化列表中调用基类构造函数初始化继承来的成员变量

时间:2022-07-15 20:02:56
今天有人问道:在实例子类对象时,会先调用父类的构造函数,然后再调用子类的构造函数。
可为什么在有些子类中构造函数的参数化列表中还要调用父类的构造函数?
先看下面代码:
 
class Base
{
public:
int m_nValue;

Base(int nValue=0)
: m_nValue(nValue)
{
}
};

class Derived: public Base
{
public:
double m_dValue;

Derived(double dValue=0.0)
: m_dValue(dValue)
{
}
};
当不牵扯到继承时,构造函数只需要关心自己的成员变量就行了。例如上面的基类base就可以这样构造对象:
int main()
{
Base cBase(5); // use Base(int) constructor

return 0;
}
在这个过程中,c++究竟做了些什么:
 
 
  1. cBase对象分配内存
  2. 调用合适的Base构造函数
  3. 初始化成员列表对成员变量进行初始化
  4. 执行构造函数
  5. 控制权返回给调用者

但是继承类构造对象时就有点复杂了:

int main()
{
Derived cDerived(1.3); // use Derived(double) constructor

return 0;
}
构造子类对象时具体c++具体做了:

  1. 为子类对象cDerived 分配内存(为父类和子类分配足够的内存)
  2. 调用合适的子类构造函数
  3. 先用合适的基类构造函数构造基类对象
  4. 初始化列表对成员函数进行初始化
  5. 执行构造函数
  6. 返回

记住很重要的一点事:构造子类时,基类构造函数先要被调用(调用无参数的默认构造函数),在此之前子类构造函数什么都不能做。

初始化基类成员函数

我们的子类这样定义有一个很大的弊端就是不能初始化继承来的m_nValue变量。如果在构造子类对象时,想同时设置子类变量m_dValue和父类变量m_nValue的值,该怎么办呢?

c++新手可以这么设计子类:

class Derived: public Base
{
public:
double m_dValue;

Derived(double dValue=0.0, int nValue=0)
// does not work
: m_dValue(dValue), m_nValue(nValue)
{
}
};
这么个想法很好,我们的确在需要一个参数来指定基类成员变量m_nValue的值。

但是,c++不运行在子类构造函数的初始化成员列表中对父类的成员变量进行初始化。换句话说,初始化成员列表只能初始化本类的成员变量。

为什么c++这么规定呢?原因是要考虑到const变量和reference变量。如果变量m_nValue是const,赋值不可改变,该变量在定义的时候就必须初始化。父类对m_nValue定义是赋值,而子类构造函数再去对它初始化,那么这个const变量就有可能会别改变。为了防止这种情况的发生,c++才作此规定。

那么怎么才能在构造子类对象时合适地对继承来的变量m_nValue赋值呢?

可用在子类的构造函数的初始化成员列表中调用父类的构造函数。

class Derived: public Base
{
public:
double m_dValue;

Derived(double dValue=0.0, int nValue=0)
: Base(nValue), // Call Base(int) constructor with value nValue!
m_dValue(dValue)
{
}
};

现在:

int main()
{
Derived cDerived(1.3, 5); // use Derived(double) constructor

return 0;
}

执行细节:

  1. 为子类对象cDerived分配内存
  2. 调用子类构造函数,发现有dValue = 1.3,  nValue = 5
  3. 编译器查找基类是否有特殊的构造函数,有!那就掉用参数nValue = 5的构造函数Base(int)
  4. 基类初始化参数列表将基类成员变量m_nValue 设置为 5
  5. 执行基类构造函数体
  6. 基类构造函数返回
  7. 子类参数化列表设置成员变量m_dValue 为 1.3
  8. 执行子类构造函数体
  9. 子类构造函数返回