C++虛函數(shù)注意事項
文章轉(zhuǎn)自公眾號:Coder梁(ID:Coder_LT)
一、虛函數(shù)注意事項
在之前的文章當(dāng)中,我們已經(jīng)討論了虛函數(shù)的使用方法,也對它的原理進行了簡單的介紹。
這里簡單做一個總結(jié):
- 在基類的方法聲明中使用關(guān)鍵字virtual可以聲明虛函數(shù)
- 加上了virtual關(guān)鍵字的函數(shù)在基類以及派生類和派生類再派生出來的類中都是虛的
- 在調(diào)用虛函數(shù)時,程序?qū)鶕?jù)對象的類型執(zhí)行對應(yīng)的方法而非引用或指針的類型
- 在定義基類時,需要將要在派生類中重新定義的類方法聲明為虛,如析構(gòu)函數(shù)
除了這些之外,我們還有一些其他需要注意的事項。
1.構(gòu)造函數(shù)
構(gòu)造函數(shù)不能是虛函數(shù),創(chuàng)建派生類對象時將調(diào)用派生類的構(gòu)造函數(shù),而非基類的構(gòu)造函數(shù),畢竟構(gòu)造函數(shù)是根據(jù)類名調(diào)用的。
一般我們會在派生類中調(diào)用基類的構(gòu)造函數(shù),這其實不是繼承機制,所以將類構(gòu)造函數(shù)聲明為虛沒有意義。
2.析構(gòu)函數(shù)
前文說過析構(gòu)函數(shù)應(yīng)該是虛函數(shù),除非類不被繼承。
因為派生類當(dāng)中往往含有獨有的成員變量,如果析構(gòu)函數(shù)非虛,那么會導(dǎo)致在對象析構(gòu)時僅調(diào)用基類的析構(gòu)函數(shù),從而導(dǎo)致獨有的成員變量內(nèi)存不被釋放,引起內(nèi)存泄漏。
所以通常我們會將析構(gòu)函數(shù)設(shè)置成virtual,即使不用做基類也不會引起錯誤,至多只會影響一點效率。但在大型合作開發(fā)的項目當(dāng)中,許多組件和類都是共享的,我們往往無法保證我們開發(fā)的類是否會被其他開發(fā)者繼承,因此設(shè)置虛析構(gòu)函數(shù)也是一種常規(guī)做法。
3.友元
友元函數(shù)不能是虛函數(shù),因為友元不是類成員,只有成員函數(shù)才能是虛函數(shù)。
如果我們希望友元函數(shù)也能實現(xiàn)類似虛函數(shù)的功能, 我們可以在友元函數(shù)當(dāng)中使用虛函數(shù)來解決。
4.沒有重新定義
如果派生類當(dāng)中沒有重新定義虛函數(shù),那么將使用該函數(shù)的基類版本。如果派生類位于派生鏈中,如B繼承了A,C繼承了B這種情況,那么派生類將會使用最新的虛函數(shù)版本。
5.重新定義將隱藏方法
我們來看一個例子:
class Mammal {
?private:
? string name;
?public:
? Mammal(string n): name(n) {}
? virtual void speak() const {
? ?cout << "can't say anything" << endl;
? }
};
class Human : public Mammal{
?private:
? string job;
?public:
? Human(string n, string j): Mammal(n), job(j) {}
? virtual void speak(const string st) const {
? ?cout << "i'm human" << endl;
? }
};我們在父類當(dāng)中定義了一個無參虛函數(shù)speak,而在子類Human當(dāng)中也定義了一個需要傳入一個string類型的虛函數(shù)speak。
我試了一下,在我的g++編譯器當(dāng)中,會報錯:

但根據(jù)C++ Primer中的說法,在一些古老的編譯器當(dāng)中,可能不會報錯,甚至可能連警告都沒有。
在這類編譯器當(dāng)中,我們重新定義父類中的虛函數(shù),這樣的重新定義不會生成兩個重載版本,而是隱藏了父類無參的版本,只保留了接受string類型的版本,這種情況有別于函數(shù)重載。
在派生類當(dāng)中重新定義函數(shù),不是使用相同的函數(shù)特征標(biāo)覆蓋基類聲明,而是隱藏同名的基類方法,不管函數(shù)特征標(biāo)如何。
C++ Primer當(dāng)中給出了兩條經(jīng)驗規(guī)則:
如果重新定義繼承的方法,應(yīng)該保證與原來的原型完全相同,唯一的例外是返回的類型,如果基類返回的是基類的引用或指針,派生類可以改成派生類的引用或指針:
class Mammal {
?private:
? string name;
?public:
? Mammal(string n): name(n) {}
? virtual Mammal* build();
};
class Human : public Mammal{
?private:
? string job;
?public:
? Human(string n, string j): Mammal(n), job(j) {}
? virtual Human* build();
};如果基類聲明被重載了,那么應(yīng)該在派生類中聲明所有的基類版本:
class Mammal {
?private:
? string name;
?public:
? Mammal(string n): name(n) {}
? virtual void speak() const ;
? ? ?virtual void speak(int n) const;
? ? ?virtual void speak(const string st) const;
};
class Human : public Mammal{
?private:
? string job;
?public:
? Human(string n, string j): Mammal(n), job(j) {}
? virtual void speak() const ;
? ? ?virtual void speak(int n) const;
? ? ?virtual void speak(const string st) const;
};如果我們只重新定義了一個版本,那么另外兩個版本將隱藏。
但這可能和編譯器版本有關(guān),在新版的編譯器當(dāng)中似乎取消了這一設(shè)定。
我嘗試了一下,發(fā)現(xiàn)并不會隱藏,一樣可以順利調(diào)用父類方法。
class Mammal {
?private:
? string name;
?public:
? Mammal(string n): name(n) {}
? virtual void speak() const {
? ?cout << "can't say anything from empty" << endl;
? }
? virtual void speak(const string st) const {
? ?cout << "can't say anything from string input" << endl;
? }
};
class Human : public Mammal{
?private:
? string job;
?public:
? Human(string n, string j): Mammal(n), job(j) {}
? virtual void speak(const string st) const {
? ?cout << "i'm human" << endl;
? }
};
int main() {
?Mammal *m = new Human("man", "spiderman");
?m->speak();
?return 0;
}到此這篇關(guān)于EC++虛函數(shù)注意事項的文章就介紹到這了,更多相關(guān)EC++虛函數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++中的數(shù)字轉(zhuǎn)字符串to_string
這篇文章主要介紹了C++中的數(shù)字轉(zhuǎn)字符串to_string,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-11-11
Qt 使用QDialog實現(xiàn)界面遮罩的示例(蒙版)
界面遮罩在很多時候都可以用到,例如彈窗,本文主要介紹了Qt 使用QDialog實現(xiàn)界面遮罩的示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04
C/C++ 中sizeof(''a'')對比詳細(xì)介紹
這篇文章主要介紹了C/C++ 中sizeof('a')的值對比詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下2017-02-02

