一、繼承
繼承允許我們依據(jù)一個(gè)類來定義另一個(gè)類,這使得創(chuàng)建和維護(hù)一個(gè)應(yīng)用程序變得更容易。這樣做也達(dá)到了重用代碼功能和提高執(zhí)行效率的效果。
派生類的成員可以直接訪問基類的保護(hù)成員(protected),但不能直接訪問基類的私有成員(private)。不過需要注意的是,派生類的成員函數(shù)只能訪問所作用的那個(gè)對(duì)象(即this指針指向的對(duì)象)的基類保護(hù)成員,不能訪問其他基類對(duì)象的基類保護(hù)成員(比如通過函數(shù)參數(shù)傳來的基類對(duì)象)。
繼承分為公有繼承、保護(hù)繼承與私有繼承,除了公有繼承,剩下兩個(gè)很少用到,三者區(qū)別如下:
二、函數(shù)重載、隱藏、覆蓋、重寫
1.函數(shù)重載(Function Overload)
C++規(guī)定在同一作用域中,函數(shù)名相同但函數(shù)特征標(biāo)(即參數(shù)個(gè)數(shù)、類型、順序)不同時(shí),構(gòu)成函數(shù)重載。
函數(shù)重載的注意事項(xiàng):
- 返回值類型不能作為重載的標(biāo)準(zhǔn)。
- 參數(shù)是否為引用不能作為重載的標(biāo)準(zhǔn),盡管某些時(shí)候能通過編譯,但在調(diào)用時(shí)會(huì)產(chǎn)生二義性。
- 成員函數(shù)是否被static修飾也不能作為重載的標(biāo)準(zhǔn),因?yàn)?strong>在通過實(shí)例化后的對(duì)象調(diào)用方法時(shí)無法區(qū)分是否要調(diào)用靜態(tài)成員函數(shù)。
-
一個(gè)函數(shù)不能既作為重載函數(shù),又作為有默認(rèn)參數(shù)的函數(shù),因?yàn)楫?dāng)調(diào)用函數(shù)時(shí)如果少寫一個(gè)參數(shù),系統(tǒng)無法判定是利用重載函數(shù)還是利用默認(rèn)參數(shù)的函數(shù),即
int func(int a)
和int func(int a = 0)
是不能在同一作用域中同時(shí)存在的。
這里還要特別注意一下const修飾函數(shù)或函數(shù)參數(shù)時(shí)的情況:
class A {
public:
/**
* 不管形參有沒有const修飾實(shí)參都不會(huì)被修改,二者在調(diào)用時(shí)沒有區(qū)別,因此不能構(gòu)成重載
*/
void func(int a);
void func(const int a); // NO
/**
* 由底層const修飾的指針指向的實(shí)參不能被修改,二者在調(diào)用時(shí)存在區(qū)別,因此可以構(gòu)成重載
*/
void func_bot_p(int *a);
void func_bot_p(const int *a); // YES
/**
* 不管有沒有頂層const修飾,該指針指向的內(nèi)容都可以被修改,二者在調(diào)用時(shí)沒有區(qū)別,因此不能構(gòu)成重載
*/
void func_top_p(int *a);
void func_top_p(int *const a); // NO
/**
* 在const修飾引用時(shí)實(shí)參不能被修改,二者在調(diào)用時(shí)存在區(qū)別,因此可以構(gòu)成重載
*/
void func_ref(int &a);
void func_ref(const int &a); // YES
/**
* 由const修飾的成員函數(shù)只能由const對(duì)象調(diào)用,二者在調(diào)用時(shí)存在區(qū)別,因此可以構(gòu)成重載
*/
void func_ret(int a);
void func_ret(int a) const; // YES
};
2.函數(shù)隱藏(Function Hiding)
不同作用域中定義的同名函數(shù)會(huì)構(gòu)成函數(shù)隱藏(不要求函數(shù)返回值和函數(shù)參數(shù)類型相同)。
類成員函數(shù)會(huì)屏蔽全局函數(shù),派生類成員函數(shù)會(huì)屏蔽與其同名的基類成員函數(shù)(但如果該基類成員函數(shù)為虛函數(shù),且函數(shù)返回值和特征標(biāo)相同則構(gòu)成函數(shù)重寫)。
#include <iostream>
using namespace std;
void func() {
cout << "global::func()" << endl;
}
class A {
public:
/**
* 隱藏了外部的func
*/
void func() {
cout << "A::func()" << endl;
}
void use_func() {
func();
::func(); // 使用全局函數(shù)時(shí)要加作用域
}
};
class B : public A {
public:
/**
* 隱藏了基類的func
*/
void func() {
cout << "B::func()" << endl;
}
void use_func() {
func();
A::func(); // 使用基類函數(shù)時(shí)要加作用域
}
};
int main() {
A a;
B b;
a.use_func();
b.use_func();
}
atreus@MacBook-Pro % g++ main.cpp -o main -std=c++11
atreus@MacBook-Pro % ./main
A::func()
global::func()
B::func()
A::func()
atreus@MacBook-Pro %
3.函數(shù)重寫與函數(shù)覆蓋(Function Override)
派生類中與基類同返回值類型、同名和同特征標(biāo)的虛函數(shù)重定義,構(gòu)成虛函數(shù)覆蓋,也叫虛函數(shù)重寫。
需要注意的是,在默認(rèn)情況下,如果重新定義了繼承的方法,應(yīng)確保與原來的原型完全相同,但如果返回類型是基類引用或指針,則可以修改為指向派生類的引用或指針,這種新出現(xiàn)的特性叫做返回類型協(xié)變(covariance of return type)。
#include <iostream>
using namespace std;
class A {
public:
void func() {
cout << "A::func()" << endl;
}
virtual void func_v() {
cout << "A::func_v()" << endl;
}
};
class B : public A {
public:
/* 函數(shù)隱藏 */
void func() {
cout << "B::func()" << endl;
}
/* 函數(shù)重載 */
void func_v() override {
cout << "B::func_v()" << endl;
}
};
int main() {
A *a = new B;
a->func();
a->func_v();
delete a;
}
atreus@MacBook-Pro % g++ main.cpp -o main -std=c++11
atreus@MacBook-Pro % ./main
A::func()
B::func_v()
atreus@MacBook-Pro %
三、多態(tài)
多態(tài)是指一個(gè)方法同時(shí)具有多種形態(tài),具體形態(tài)取決于調(diào)用該方法的具體對(duì)象。從實(shí)現(xiàn)的角度可以將多態(tài)分為編譯時(shí)多態(tài)(主要通過函數(shù)模板、函數(shù)重載和運(yùn)算符重載實(shí)現(xiàn))和運(yùn)行時(shí)多態(tài)(主要通過虛函數(shù)和函數(shù)重寫實(shí)現(xiàn))。
對(duì)于運(yùn)行時(shí)多態(tài),其實(shí)現(xiàn)主要有三個(gè)前提:
- 存在繼承。
- 存在函數(shù)重寫(覆蓋)。
- 存在基類指針或者引用指向子類對(duì)象。
運(yùn)行時(shí)多態(tài)的實(shí)現(xiàn)要借助于動(dòng)態(tài)綁定,動(dòng)態(tài)綁定借助于虛函數(shù)實(shí)現(xiàn),虛函數(shù)的限制如下:
- 只有類的成員函數(shù)才能聲明為虛函數(shù)。
- 基類的析構(gòu)函數(shù)可以是虛函數(shù)且通常聲明為虛函數(shù)。
- 構(gòu)造函數(shù)不能為虛函數(shù)。
- 內(nèi)聯(lián)函數(shù)不能是虛函數(shù)。
- 靜態(tài)成員函數(shù)不能是虛函數(shù)。
虛函數(shù)、虛函數(shù)表及虛函數(shù)實(shí)現(xiàn)多態(tài)的原理
其中,動(dòng)態(tài)綁定是運(yùn)行時(shí)綁定,通過地址實(shí)現(xiàn),它是指基類的指針或引用有可能指向不同的派生類對(duì)象。對(duì)于非虛函數(shù),執(zhí)行時(shí)實(shí)際調(diào)用該函數(shù)的對(duì)象類型即為該指針或引用的靜態(tài)類型。而對(duì)于虛函數(shù),執(zhí)行時(shí)實(shí)際調(diào)用該函數(shù)的對(duì)象類型為該指針或引用所指對(duì)象的實(shí)際類型。
四、純虛函數(shù)和抽象類
當(dāng)類聲明中包含純虛函數(shù)(定義是末尾有 = 0
的虛函數(shù))時(shí),則不能創(chuàng)建該類的對(duì)象,這個(gè)類變?yōu)?strong>抽象類,C++中的抽象類類似于Java中的接口,抽象類必須至少包含一個(gè)純虛函數(shù)。
此外,對(duì)于抽象類還有以下注意事項(xiàng):
- 抽象類只能用作其他類的基類,當(dāng)然也可以作為另一個(gè)抽象類的基類。
- 抽象類不能用來定義對(duì)象,不能實(shí)例化,也不能用作參數(shù)類型、函數(shù)返回類型或顯式轉(zhuǎn)換的類型。
- 如果一個(gè)非抽象類從抽象類中派生,則其必須通過覆蓋來實(shí)現(xiàn)所有的繼承而來的抽象成員函數(shù)。
#include <iostream>
/* 抽象類 */
class Car {
public:
virtual void showName() = 0; // 純虛函數(shù)
};
class Audi : public Car {
public:
void showName() override { std::cout << "Audi" << std::endl; }
};
class Volvo : public Car {
public:
void showName() override { std::cout << "Volvo" << std::endl; }
};
int main() {
Audi audi;
Volvo volvo;
audi.showName(); // Audi
volvo.showName(); // Volvo
return 0;
}
五、多重繼承的二義性(菱形繼承)
菱形繼承是指當(dāng)類B和類C同時(shí)繼承于基類A,類D同時(shí)繼承于類B和類C,此時(shí)類A中的成員變量和成員函數(shù)繼承到類D中就變成了兩份,在D中調(diào)用A中的成員會(huì)導(dǎo)致二義性,同時(shí)一個(gè)變量分兩份存儲(chǔ)也存在內(nèi)存空間浪費(fèi)的問題。
通過虛基類和虛繼承機(jī)制,可以在多繼承中只保留一份共同成員,從而解決了多繼承導(dǎo)致的命名沖突和數(shù)據(jù)冗余。
在繼承方式前面加上 virtual
關(guān)鍵字就是虛繼承,如果不采用虛繼承,在類D中使用類A中的m_a時(shí)則需要通過 B::m_a
或 C::m_a
來指定具體使用哪個(gè)m_a。
#include <iostream>
using namespace std;
class A {
protected:
int m_a = 0;
};
class B : virtual public A {
protected:
int m_b = 1;
};
class C : virtual public A {
protected:
int m_c = 2;
};
class D : public B, public C {
protected:
int m_d = 3;
public:
D() {
cout << m_a << endl;
cout << m_b << endl;
cout << m_c << endl;
cout << m_d << endl;
}
};
int main() {
D d;
return 0;
}
atreus@MacBook-Pro % g++ main.cpp -o main -std=c++11
atreus@MacBook-Pro % ./main
0
1
2
3
atreus@MacBook-Pro %
C++標(biāo)準(zhǔn)庫中的iostream類就是一個(gè)虛繼承的實(shí)際應(yīng)用案例。iostream從istream和ostream直接繼承而來,而istream和ostream又都繼承自一個(gè)共同的名為base_ios的類,是典型的菱形繼承。
參考:
https://cloud.tencent.com/developer/article/1177174
https://blog.csdn.net/weixin_39640298/article/details/88725073
http://c.biancheng.net/view/2280.html文章來源:http://www.zghlxwxcb.cn/news/detail-456899.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-456899.html
到了這里,關(guān)于C++ 中的繼承和多態(tài)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!