網站首頁 編程語言 正文
文章轉自公眾號:Coder梁(ID:Coder_LT)
一、虛函數注意事項
在之前的文章當中,我們已經討論了虛函數的使用方法,也對它的原理進行了簡單的介紹。
這里簡單做一個總結:
- 在基類的方法聲明中使用關鍵字virtual可以聲明虛函數
- 加上了virtual關鍵字的函數在基類以及派生類和派生類再派生出來的類中都是虛的
- 在調用虛函數時,程序將會根據對象的類型執行對應的方法而非引用或指針的類型
- 在定義基類時,需要將要在派生類中重新定義的類方法聲明為虛,如析構函數
除了這些之外,我們還有一些其他需要注意的事項。
1.構造函數
構造函數不能是虛函數,創建派生類對象時將調用派生類的構造函數,而非基類的構造函數,畢竟構造函數是根據類名調用的。
一般我們會在派生類中調用基類的構造函數,這其實不是繼承機制,所以將類構造函數聲明為虛沒有意義。
2.析構函數
前文說過析構函數應該是虛函數,除非類不被繼承。
因為派生類當中往往含有獨有的成員變量,如果析構函數非虛,那么會導致在對象析構時僅調用基類的析構函數,從而導致獨有的成員變量內存不被釋放,引起內存泄漏。
所以通常我們會將析構函數設置成virtual,即使不用做基類也不會引起錯誤,至多只會影響一點效率。但在大型合作開發的項目當中,許多組件和類都是共享的,我們往往無法保證我們開發的類是否會被其他開發者繼承,因此設置虛析構函數也是一種常規做法。
3.友元
友元函數不能是虛函數,因為友元不是類成員,只有成員函數才能是虛函數。
如果我們希望友元函數也能實現類似虛函數的功能, 我們可以在友元函數當中使用虛函數來解決。
4.沒有重新定義
如果派生類當中沒有重新定義虛函數,那么將使用該函數的基類版本。如果派生類位于派生鏈中,如B繼承了A,C繼承了B這種情況,那么派生類將會使用最新的虛函數版本。
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; ? } };
我們在父類當中定義了一個無參虛函數speak
,而在子類Human
當中也定義了一個需要傳入一個string
類型的虛函數speak
。
我試了一下,在我的g++編譯器當中,會報錯:
但根據C++ Primer
中的說法,在一些古老的編譯器當中,可能不會報錯,甚至可能連警告都沒有。
在這類編譯器當中,我們重新定義父類中的虛函數,這樣的重新定義不會生成兩個重載版本,而是隱藏了父類無參的版本,只保留了接受string
類型的版本,這種情況有別于函數重載。
在派生類當中重新定義函數,不是使用相同的函數特征標覆蓋基類聲明,而是隱藏同名的基類方法,不管函數特征標如何。
C++ Primer當中給出了兩條經驗規則:
如果重新定義繼承的方法,應該保證與原來的原型完全相同,唯一的例外是返回的類型,如果基類返回的是基類的引用或指針,派生類可以改成派生類的引用或指針:
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(); };
如果基類聲明被重載了,那么應該在派生類中聲明所有的基類版本:
?
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; };
如果我們只重新定義了一個版本,那么另外兩個版本將隱藏。
但這可能和編譯器版本有關,在新版的編譯器當中似乎取消了這一設定。
我嘗試了一下,發現并不會隱藏,一樣可以順利調用父類方法。
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; }
相關推薦
- 2022-09-14 jquery實現計算器小功能_jquery
- 2022-06-06 Rust字符串字面值的一些經驗總結_相關技巧
- 2022-06-21 解決Git?Bash中文亂碼的問題_其它綜合
- 2023-01-05 Kotlin的空安全處理方式詳解_Android
- 2022-11-07 python中openpyxl庫用法詳解_python
- 2022-08-22 Nginx配置使用詳解_nginx
- 2023-10-16 獲取當月的月初和月末日期時間戳
- 2022-12-29 Android?Activity狀態與操作探究_Android
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支