c++继承构造函数
【1】為什么需要繼承構(gòu)造函數(shù)?
首先,看看如下代碼片段:
1 struct A2 { 3 A(int i)4 {} 5 }; 6 7 struct B : A8 { 9 B(int i) : A(i) 10 {} 11 };在C++中非常常見:B派生于A,B在構(gòu)造函數(shù)中調(diào)用A的構(gòu)造函數(shù),從而完成構(gòu)造函數(shù)的“傳遞”。
有時候,基類可能擁有數(shù)量眾多的不同版本的構(gòu)造函數(shù)(這樣的情況并不少見)。
那么,倘若基類中有大量的構(gòu)造函數(shù),而派生類卻只有一些成員函數(shù)時,那么對于派生類而言,其構(gòu)造就等同于構(gòu)造基類。
為了遵從語法規(guī)則,就需要寫很多的“透傳”的構(gòu)造函數(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() {} };在構(gòu)造B的時候想要擁有A這樣多的構(gòu)造方法的話,就必須一—“透傳”各個接口。這無疑是相當(dāng)不方便的。
而引入繼承構(gòu)造函數(shù)的機(jī)制就是為了解決這種麻煩的。
【2】using聲明
我們知道C++中已經(jīng)有一個好用的規(guī)則:
如果派生類要使用基類的成員函數(shù)(被隱藏)的話,可以通過using聲明(using-declaration)來完成。請看如下示例:
(1)不聲明基類成員函數(shù):
基類成員函數(shù)被隱藏(關(guān)于隱藏可以參考《重載、覆蓋、隱藏》),在派生類中不做聲明:
1 #include <iostream>2 using namespace std;3 4 struct Base5 { 6 void f(double i)7 { 8 cout << "Base:" << i << endl;9 } 10 }; 11 12 struct Derived : Base 13 { 14 // using Base::f; // 聲明基類Base的f函數(shù) 15 void f(int i) 16 { 17 cout << "Derived:" << i << endl; 18 } 19 }; 20 21 int main() 22 { 23 Base b; 24 b. f(4.5); 25 Derived d; 26 d. f(4.5); 27 } 28 29 /* 30 Base:4.5 31 Derived:4 32 */編譯警告:warning C4244 : “參數(shù)”: 從“double”轉(zhuǎn)換到“int”,可能丟失數(shù)據(jù)
再結(jié)合運(yùn)行結(jié)果分析,說明執(zhí)行的是派生類Derived中參數(shù)為int類型的函數(shù)f。
(2)聲明基類成員函數(shù):
1 #include <iostream>2 using namespace std;3 4 struct Base5 { 6 void f(double i)7 { 8 cout << "Base:" << i << endl;9 } 10 }; 11 12 struct Derived : Base 13 { 14 using Base::f; // 聲明繼承基類Base的f函數(shù) 15 void f(int i) 16 { 17 cout << "Derived:" << i << endl; 18 } 19 }; 20 21 int main() 22 { 23 Base b; 24 b. f(4.5); 25 Derived d; 26 d. f(4.5); 27 } 28 29 /* 30 Base:4.5 31 Base:4.5 32 */編譯無警告,再結(jié)合運(yùn)行結(jié)果分析:
說明執(zhí)行的是基類Base中參數(shù)為double類型的函數(shù)f。
同時,可以看到派生類Derived中其實(shí)有兩個f函數(shù)的版本。
經(jīng)過以上的分析,在C++11中,這個特性被擴(kuò)展到了構(gòu)造函數(shù)上。
即派生類可以通過使用using聲明來繼承基類的構(gòu)造函數(shù)。
【3】應(yīng)用繼承構(gòu)造函數(shù)
如上示例1,應(yīng)用繼承構(gòu)造函數(shù),改造如下:
1 #include <iostream>2 using namespace std;3 4 struct A5 {6 A(int i = 10) : m_a(i)7 {}8 9 int m_a; 10 }; 11 12 struct B : A 13 { 14 using A::A; // 繼承構(gòu)造函數(shù) 15 16 int m_b{ 100 }; 17 }; 18 19 int main() 20 { 21 B b; 22 cout << b.m_a << endl; // 10 23 cout << b.m_b << endl; // 100 24 25 B bb(200); 26 cout << bb.m_a << endl; // 200 27 cout << bb.m_b << endl; // 100 28 }通過using A::A的聲明,把基類中的構(gòu)造函數(shù)全部繼承到派生類B中。
應(yīng)用注意項(xiàng):
(1)C++11標(biāo)準(zhǔn)更精巧的是,繼承構(gòu)造函數(shù)被設(shè)計(jì)為跟派生類中的各種類默認(rèn)函數(shù)(默認(rèn)構(gòu)造、析構(gòu)、拷貝構(gòu)造等)一樣,是隱式聲明的。
這意味著如果一個繼承構(gòu)造函數(shù)不被相關(guān)代碼使用,編譯器不會為其產(chǎn)生真正的函數(shù)代碼。
這無疑比“透傳”方案總是生成派生類的各種構(gòu)造函數(shù)更加節(jié)省目標(biāo)代碼空間。
(2)繼承構(gòu)造函數(shù)只會初始化基類中成員變量,對于派生類中的成員變量,則無能為力。
不過配合C++11中類成員的初始化表達(dá)式,為派生類成員變量設(shè)定一個默認(rèn)值還是沒有問題的。
比如此例中的m_b成員變量,設(shè)定默認(rèn)值為100。
(3)基類構(gòu)造函數(shù)的參數(shù)可能會有默認(rèn)值。
對于繼承構(gòu)造函數(shù)來講,基類構(gòu)造函數(shù)參數(shù)的默認(rèn)值是不會被繼承的。
比如此例中的bb對象,利用實(shí)參200進(jìn)行構(gòu)造對象,結(jié)果m_a的值為200,而不是默認(rèn)的10。
(4)基類構(gòu)造函數(shù)有默認(rèn)值會導(dǎo)致基類產(chǎn)生多個構(gòu)造函數(shù)的版本,這些版本都會被派生類繼承。
比如此例中,事實(shí)上,構(gòu)建對象b,使用的均是默認(rèn)構(gòu)造函數(shù);構(gòu)建對象bb,使用的均是帶一個參數(shù)的構(gòu)造函數(shù)版本。
(5)有的時候,還會遇到繼承構(gòu)造函數(shù)“沖突”的情況。這通常發(fā)生在派生類擁有多個基類的時候。
多個基類中的部分構(gòu)造函數(shù)可能導(dǎo)致派生類中的繼承構(gòu)造函數(shù)的函數(shù)名、參數(shù)(有的時候,我們也稱其為函數(shù)簽名)都相同,
那么繼承類中的沖突的繼承構(gòu)造函數(shù)將導(dǎo)致不合法的派生類代碼,如下示例:
1 struct A { A(int) {} }; 2 3 struct B { B(int) {} }; 4 5 struct C : A, B 6 { 7 using A::A; 8 using B::B; 9 };這種情況下,可以通過顯式定義繼承類的沖突的構(gòu)造函數(shù),阻止隱式生成相應(yīng)的繼承構(gòu)造函數(shù)來解決沖突:
1 struct A { A(int) {} }; 2 3 struct B { B(int) {} }; 4 5 struct C : A, B 6 { 7 C(int c) : A(c), B(c) 8 {} 9 };(6)如果基類的構(gòu)造函數(shù)被聲明為私有成員函數(shù),或者派生類是從基類中虛繼承的,那么就不能夠在派生類中聲明繼承構(gòu)造函數(shù)。
(7)如果基類的構(gòu)造函數(shù)沒有默認(rèn)構(gòu)造函數(shù),那么一旦使用了繼承構(gòu)造函數(shù),編譯器也不會再為派生類生成默認(rèn)構(gòu)造函數(shù)。
如下示例不能夠通過編譯:
1 struct A 2 { 3 A(int) {} 4 };5 6 struct B : A 7 { 8 using A::A; 9 }; 10 11 int main() 12 { 13 B objB; // C2280 “B::B(void)”: 嘗試引用已刪除的函數(shù) 14 }個人認(rèn)為,解決這種問題有兩種方式,代碼如下:
1 #if 0 // 方式一:基類定義默認(rèn)構(gòu)造函數(shù),派生類自然也就有了2 struct A { A(int a = 10) {} };3 struct B : A4 {5 using A::A;6 };7 #else // 方式二:派生類自定義默認(rèn)構(gòu)造函數(shù)8 struct A { A(int) {} };9 struct B : A 10 { 11 using A::A; 12 B(int c = 20) : A(c) 13 {} 14 }; 15 #endif 16 17 int main() 18 { 19 B objB; 20 }如上所述。
good good study, day day up.
順序 選擇 循環(huán) 總結(jié)
總結(jié)
- 上一篇: c++primer 5th第15章基础、
- 下一篇: c++primer 12.3.1文本查询