繼承構(gòu)造函數(shù)
類具有可派生性,派生類可以自動(dòng)的獲取基類的成員變量和接口(虛函數(shù)和純虛函數(shù),public派生)。不過基類的廢墟函數(shù)則無法再被派生類使用了。這條規(guī)則對(duì)于構(gòu)造函數(shù)也不例外。如果派生類要使用基類的構(gòu)造函數(shù),通常需要在構(gòu)造函數(shù)中顯式聲明。
struct A {
A(int i) {}
};
struct B: A {
B(int i) : A(i) {};
}
如果基類有數(shù)量較多的不同版本的構(gòu)造函數(shù),但是派生類卻只有少量的構(gòu)造函數(shù)時(shí)。這時(shí),這種透傳的方案就顯得不是方便。
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(){}
};
通過使用 using 聲明(using-declaration)來完成。
#include <iostream>
using namespace std;
struct Base {
void f(double i) { cout << "Base:" << i << endl; }
};
struct Derived : Base {
using Base::f;
/*
* 聲明派生類中使用基類的函數(shù) f
* 這樣一來,派生類就有了兩個(gè) f 函數(shù)的版本。
*/
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
}
using 使用在構(gòu)造函數(shù)上:
struct A {
A(int i) {}
A(double d, int i) {}
A(float f, int i, const char* c) {}
// ...
};
struct B : A {
using A::A; // 繼承構(gòu)造函數(shù)
// ...
virtual void ExtraInterface(){}
};
C++11 標(biāo)準(zhǔn)繼承構(gòu)造函數(shù)被設(shè)計(jì)為跟派生類中的默認(rèn)函數(shù)(默認(rèn)構(gòu)造,析構(gòu),拷貝構(gòu)造等)一樣,是隱式聲明的。這意味著如果一個(gè)繼承構(gòu)造函數(shù)不被相關(guān)代碼使用,編譯器不會(huì)為其產(chǎn)生真正的函數(shù)代碼。
不過繼承構(gòu)造只會(huì)初始化基類的成員變量,對(duì)于派生類中的成員變量則無能為力。
struct A {
A(int i) {}
A(double d, int i) {}
A(float f, int i, const char* c) {}
// ...
};
struct B : A {
using A::A;
int d {0};
};
int main() {
B b(356); // b.d被初始化為0
}
有時(shí),基類構(gòu)造函數(shù)參數(shù)會(huì)有默認(rèn)值,對(duì)于繼承構(gòu)造函數(shù)來講,參數(shù)的默認(rèn)值是不會(huì)被繼承的。事實(shí)上,默認(rèn)值會(huì)導(dǎo)致基類產(chǎn)生多個(gè)構(gòu)造函數(shù)的版本,這些函數(shù)版本都會(huì)被派生類繼承。
struct A {
A(int a = 3, double = 2.4) {}
};
struct B : A {
using A::A;
};
事實(shí)上,A會(huì)產(chǎn)生如下構(gòu)造函數(shù):
- A(int a = 3, double = 2.4)
- A(int a = 3)
- A(const A&) 默認(rèn)的復(fù)制構(gòu)造
- A()
相應(yīng)的,B中的構(gòu)造函數(shù)也會(huì)包括如下:
- B(int , double) 繼承構(gòu)造
- B(int) 繼承構(gòu)造
- B(const B&) 復(fù)制構(gòu)造,不是繼承來的
- B() 默認(rèn)構(gòu)造
繼承構(gòu)造函數(shù)“沖突”的情況:
多個(gè)基類中的部分構(gòu)造函數(shù)可能導(dǎo)致派生類中的繼承構(gòu)造函數(shù)的函數(shù)名,參數(shù)都相同,那么繼承類中的沖突的繼承構(gòu)造函數(shù)將導(dǎo)致不合法的派生類代碼。
struct A { A(int) {} };
struct B { B(int) {} };
struct C: A, B {
using A::A;
using B::B;
};
一旦使用了繼承構(gòu)造函數(shù),那么編譯器就不會(huì)為派生類生成默認(rèn)構(gòu)造函數(shù)了。
struct A { A(int){} };
struct B: A { using A::A };
B b; // error , 沒有默認(rèn)無參構(gòu)造函數(shù)
委派構(gòu)造函數(shù)
class Info {
public:
Info() : type(1), name('a') { InitRest(); }
Info(int i) : type(i), name('a') { InitRest(); }
Info(char e): type(1), name(e) { InitRest(); }
private:
void InitRest() { /* 其它初始化 */ }
int type;
char name;
// ...
};
上述示例明顯顯得非常笨拙:
class Info {
public:
Info() { InitRest(); }
Info(int i) : type(i) { InitRest(); }
Info(char e): name(e) { InitRest(); }
private:
void InitRest() { /* 其它初始化 */ }
int type {1};
char name {'a'};
// ...
};
上述示例進(jìn)行了優(yōu)化,但是還是需要調(diào)用 InitReset() .
C++11 委派構(gòu)造函數(shù)
class Info {
public:
Info() { InitRest(); }
Info(int i) : Info() { type = i; }
Info(char e): Info() { name = e; }
/*
* 委派構(gòu)造函數(shù)不能有初始化列表
*/
private:
void InitRest() { /* 其它初始化 */ }
int type {1};
char name {'a'};
// ...
};
- 調(diào)用者 —— 委派構(gòu)造函數(shù)(delegating constructor)
- 被調(diào)用者 —— 目標(biāo)構(gòu)造函數(shù)(target constructor)
繼續(xù)改造:
class Info {
public:
Info() : Info(1, 'a') { }
Info(int i) : Info(i, 'a') { }
Info(char e): Info(1, e) { }
private:
Info(int i, char e): type(i), name(e) { /* 其它初始化 */ }
int type;
char name;
// ...
};
class Info {
public:
Info() : Info(1) { } // 委托構(gòu)造函數(shù)
Info(int i) : Info(i, 'a') { } // 既是目標(biāo)構(gòu)造函數(shù),也是委托構(gòu)造函數(shù)
Info(char e): Info(1, e) { }
private:
Info(int i, char e): type(i), name(e) { /* 其它初始化 */ } // 目標(biāo)構(gòu)造函數(shù)
int type;
char name;
// ...
};
避免形成“委托換(delegation cycle)”
struct Rule2 {
int i, c;
Rule2() : Rule2(2) {}
Rule2(int i) : Rule2('c') {}
Rule2(char c) : Rule2(2) {}
}
委派構(gòu)造函數(shù)一個(gè)實(shí)際的應(yīng)用:使用構(gòu)造模板函數(shù)產(chǎn)生目標(biāo)構(gòu)造函數(shù)
#include <list>
#include <vector>
#include <deque>
using namespace std;
class TDConstructed {
template<class T>
TDConstructed(T first, T last)
:l(first, last) {}
list<int> l;
public:
TDConstructed(vector<short> & v):
TDConstructed(v.begin(), v.end()) {}
TDConstructed(deque<int> & d):
TDConstructed(d.begin(), d.end()) {}
};
委派構(gòu)造函數(shù)在異常處理中的應(yīng)用
#include <iostream>
using namespace std;
class DCExcept {
public:
DCExcept(double d)
try : DCExcept(1, d) {
cout << "Run the body." << endl;
// 其它初始化
} catch(...) {
cout << "caught exception." << endl;
}
private:
DCExcept(int i, double d) {
cout << "going to throw!" << endl;
throw 0;
}
int type;
double data;
};
int main() {
DCExcept a(1.2);
}
從目標(biāo)函數(shù)中拋出異常,在委派構(gòu)造函數(shù)中捕獲。