1. 程式人生 > >c++11 繼承建構函式

c++11 繼承建構函式

若基類擁有數量眾多的不同版本的建構函式,而派生類中只有一些成員函式,則對於派生類而言,其建構函式就等同於構造基類。

struct A {
 A(int i) {}
 A(double d, int i) {}
 A(float f, int i, const char* c) {}
 //...
};

struct B : 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 {
 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沒有預設建構函式