若基類擁有數量眾多的不同版本的構造函數,而派生類中只有一些成員函數,則對於派生類而言,其構造函數就等同於構造基類。
struct A { A(int i) {} A(double d, int i) {} A(float f, int i, const char* c) {} //... }; struct B : public: A { B(int i): A(i) {} B(double d, int i): A(d, i) {} B(float f, int i, const char* c): A(f, i c) {} //... virtual void ExtraInterface() {} };
如上,B繼承於A,只添加了一個接口,但在構造B時想要擁有A這樣的構造方法時,就必須一一透傳各個接口。
在C++中,如果派生類想要使用基類的成員函數,可以通過using聲明來完成。如下:
#include <iostream> using namespace std; struct Base { void f(double i) { cout << "Base: " << i << endl; } }; struct Derived: Base {
public: using Base::f; void f(int i) { cout << "Derived: " << i << endl;} }; int main() { Base b; b.f(4.5); // Base: 4.5 Derived d; d.f(4.5); // Base: 4.5 d.f(4); // Derived: 4
return 0; }
基類和派生類都聲明了函數f,而使用過using聲明后,派生類也可以使用基類版本的函數f,這樣派生類中就有了兩個f函數的版本。
在c++11中,此方法擴展到構造函數上,子類可以通過using聲明來聲明繼承基類的構造函數。在剛開始的代碼可以改造成如下:
struct A { A(int i) {} A(double d, int i) {} A(float f, int i, const char* c) {} }; struct B : A { using A::A; virtual void ExtraInterface() {} };
通過 using A::A的聲明,將基類中的構造函數悉數集成到派生類B中。且標准繼承構造函數和派生類的各種類默認函數(默認構造、析構、拷貝構造等)一樣,是隱式聲明的。意味着一個繼承構造函數不被相關代碼使用,則編譯器不會為其產生真正的函數代碼。
若基類構造函數含有默認值,則對於繼承構造函數來說,參數的默認值不會被繼承,但會導致基類產生多個構造函數的版本,而這些函數版本都會被派生類繼承。
struct A { A(int a = 3, double b = 2.4); }; struct B : A{ using A::A; };
A的構造函數可能有A(int = 3, double = 2.4); A(int = 3); A(const A &); A();則相應地,B中的構造函數也會有:
B(int, double); B(int); B(const B &); B();
若碰到繼承構造函數沖突的問題,需要通過顯示定義繼承類的沖突的構造函數,阻止隱式生成相應的繼承構造函數。如下:
struct A { A(int) {} }; struct B { B(int) {} }; struct C: A, B { using A::A; using B::B; //會造成沖突 }; //使用顯示定義來解決: struct C: A, B { using A::A; using B::B; C(int) {} //顯示定義 };
注意的問題:
如果基類的構造函數被聲明為私有成員函數,或者派生類是從基類中虛繼承的,那么就不能夠在派生類中聲明繼承構造函數。且一旦使用繼承構造函數,編譯器就不會再為派生類生成默認構造函數。
struct A { A(int) {}}; struct B : A { using A::A; }; B b; //B沒有默認構造函數