继承构造函数
现实中,事物之间总存在联系,各种事物可以形成一定的层次结构,抽象到C++中来就会对应为基类和派生类的层次关系。派生类继承自基类,对于公有继承来说可以继承基类中的公有数据和方法,还可以重载或覆盖基类中的普通函数或虚函数,但是对于基类的构造函数,却不可通过继承,在派生类中完成基类数据的初始化工作。通常,在派生类中会定义若干构造函数,通过数据传递的方式,调用基类构造函数进行数据初始化。
下述代码定义了基类及派生类,通过定义带参数的派生类构造函数,完成派生类、基类数据成员的初始化,具体代码如下所示:
class Base{ //定义基类
public:
Base(int i) { n = i; }
Base(double d_value, int i) { n = i, d = d_value; }
Base(double d_value, int i, const char* p) { n = i, d = d_value, p = s; }
//…
private:
int n;
double d;
char *s;
};
class Derived:public Base //定义派生类
{
public:
Derived(int i):Base(i){}
Derived(double d, int i) :Base(d, i) {}
Derived(double f, int i, const char* p) :Base(f, i, p){}
//…
virtual void interface(){}
};
上例中,定义了基类Base和派生类Derived。基类中定义了三个构造函数,通过调用不同的基类构造函数对基类的数据成员进行了初始化,派生类中未定义数据成员,派生类的构造函数作为数据传送的通道向基类构造函数传递数据。
类似于上面的范例,派生类相对于基类只增加了一些成员函数而没有增加数据成员,对数据成员的初始化完全通过基类的构造函数完成,在派生类中不得不编写额外的构造函数实现数据传递,操作很不便捷。
C++新标准提供了继承构造函数的方法,使得派生类中可以直接使用基类的构造函数。操作方法如下面的代码所示:
class Base{
public:
Base(int i) { n = i; }
Base(double d_value, int i) { n = i, d = d_value; }
Base(double d_value, int i, const char* p) { n = i, d = d_value, p = s; }
//…
public:
int n;
double d;
char *s;
};
class Derived:public Base
{
public:
using Base::Base; //继承基类构造函数
// ...
virtual void ExtraInterface(){}
};
上述代码中,派生类添加了using Base::Base语句用于继承基类的构造函数,这样派生类就不需要再定义用于数据传递的构造函数了。此外,C++11标准中继承构造函数被设计为与派生类中的各种类的默认函数,包括默认构造函数、析构函数、拷贝构造函数一样,是隐式声明的。这表示若某个继承构造函数不被使用,编译器不会为其产生真正的函数代码。
但是,继承构造函数也有其局限性。继承构造函数是对基类构造函数的使用,因此只能初始化基类中的数据成员,对于派生类中的数据成员,则无能为力。若想初始化派生类中的数据成员,还是需要编写相应的构造函数。
对于继承构造函数来说,在使用有参数默认值的构造函数的基类时,必须小心。若有如下代码:
class Base{
public:
Base(int n = 3, double d = 3.14) {}
};
class Derived :public Base
{
using Base::Base;
};
对于上述代码,看起来派生类Derived只从基类Base中继承了一个构造函数,但是由于基类构造函数的参数具有默认值,每个默认值使用与否的不同组合都会创建出新的构造函数。对这个带有默认参数值的基类构造函数来说,派生类Derived可能从基类继承的构造函数会有多个。
首先,由于基类存在带有默认参数值的构造函数,基类可能存在的构造函数的版本有如下几个:
● Base( int n = 3, double d = 3.14 ):带有两个参数的构造函数
● Base( int n = 3 ): 带有一个参数的构造函数
● Base( const Base& ):默认的拷贝构造函数、
● Base():默认的构造函数
由于基类构造函数的版本有多个,则派生类继承了基类的构造函数后也会有多个版本:
● Derived( int, double ):带有两个参数的继承构造函数
● Derived( int ):带有一个参数的继承构造函数
● Derived( const Derived& ):默认的拷贝构造函数
● Derived():默认的构造函数
由此可知,若基类的构造函数是带有参数默认值的构造函数,会导致多个构造函数版本的产生,应特别小心。
此外,若派生类继承自多个基类,还可能遇到继承构造函数冲突的情况。多个基类中的构造函数可能导致派生类中的继承构造函数的函数名、参数相同,那么派生类中冲突的继承构造函数将导致不合法的派生类代码,如有下列代码:
class Base1{
public:
Base1(int x) {}
…
};
class Base2{
public:
Base2(int x) {}
…
};
class Derived:public Base1, public Base2
{
public:
using Base1::Base1;
using Base2::Base2;
};
上面的代码中,两个基类构造函数都拥有int类型参数,这会导致派生类中重复定义相同类型的继承构造函数。此时,可以通过显式定义派生类的构造函数,来解决这种冲突,代码如下所示:
class Derived :public Base1, public Base2
{
public:
Derived(int x):Base1(x), Base2(x){}
};
继承构造函数是C++11新标准定义的特性,可能有的编译器还没有完全支持,相信随着应用的推广,这种特性会发挥越来越重要的作用。