继承构造函数
引言
在继承体系中,假设派生类想要使用基类的构造函数,须要在构造函数中显式声明。但此时会伴随一个问题,假若基类拥有众多不同版本的构造函数。则在派生类中需要编写非常多相应的"透传"构造函数。例如:
1 struct A 2 { 3 A(int i) {} 4 A(double d, int i) {} 5 A(float f, int i, const char* c) {} 6 //...等等系列的构造函数版本号 7 }; 8 struct B :A 9 { 10 B(int i) :A(i) {} 11 B(double d, int i) :A(d, i) {} 12 B(folat f, int i, const char* c) :A(f, i, e) {} 13 //......等等好多个和基类构造函数相应的构造函数 14 };
非常明显当基类构造函数一多,派生类构造函数的写法就显得非常累赘,相当不方便。
继承构造函数
在继承关系中,有一种现象叫“OverWrite”,即在派生类中的成员函数与基类中的成员函数同名。此时,基类中的成员函数将会隐藏。
- 函数同名,参数不同
- 函数同名,参数相同,无virtual关键字修饰
如果在派生类中需要使用基类中的同名成员函数,可以使用using声明实现,例如:
1 struct A 2 { 3 void func(int a) {} 4 }; 5 6 struct B : public A 7 { 8 using A::func; //① 9 void func(const char* pBuf) {} 10 }; 11 12 int main() 13 { 14 B b; 15 b.func(10); //如果不使用using声明,此行将出错 16 }
上述思想也可应用在构造函数中:
1 struct A 2 { 3 A(int i) {} 4 A(double d, int i) {} 5 A(float f, int i, const char* c) {} 6 //...各种构造函数版本 7 }; 8 struct B :A 9 { 10 using A::A; 11 //一句话搞定所有构造函数 12 //...... 13 };
如今,通过using A::A的声明。将基类中的构造函数全继承到派生类中,更巧妙的是,假设一个继承构造函数不被相关的代码使用,编译器不会为之产生真正的函数代码,这样比透传基类各种构造函数更加节省目标代码空间。
但此时另一个问题:
当使用using语句继承基类构造函数时,派生类无法对类自身定义的新的类成员进行初始化,即无法在定义派生类构造函数,我们可使用类成员的初始化表达式,为派生类成员设定一个默认初始值。例如:
1 struct A 2 { 3 A(int i) {} 4 A(double d, int i) {} 5 A(float f, int i, const char* c) {} 6 }; 7 8 struct B : public A 9 { 10 using A::A; 11 float m_f = 10.0f; //使用默认值 12 };
在多继承下,如果多个父类拥有相同参数类型的构造函数,会造成继承构造函数冲突的情况,例如:
1 struct A { A(int i) { } }; 2 3 struct B { B(int i) { } }; 4 5 struct C : public A, public B 6 { 7 using A::A; 8 using B::B; 9 };
此时,可以显式定义继承类的冲突的构造函数,阻止隐式生成相应的继承构造函数来解决冲突。例如:
1 struct C : public A, public B 2 { 3 using A::A; 4 using B::B; 5 C(int) { } //显示定义 6 };
有几个点需要注意:
- 一旦使用了继承构造函数,编译器就不会再为派生类生成默认构造函数。
- 如果父类的构造函数被声明为私有,或者派生类是从基类中虚继承的,无法声明继承构造函数。
1 class A 2 { 3 A(int a) {} 4 }; 5 6 class B : public A 7 { 8 public: 9 using A::A; 10 }; 11 12 int main() 13 { 14 B b; //error 无法引用"B"的默认构造函数,它是已删除的函数 15 }
参考
深入理解C++11:C++11新特性解析与应用