C++ 什麼時候不應該用虛函數?
Java 里基本都是虛函數,似乎也沒什麼不行的。那麼非虛函數有什麼應用場景呢?C# 改變這一點是出於什麼考慮?
虛函數有成本,分別是運行時間(間接函數調用、也不能內聯)及空間(每個實例的虛表指針、每個類的虛表)。這些成本對於一些軟體可能不重要,但對於性能和內存要求高的情況就是重要的。總言之,真的需要運行時多態,才使用虛函數。
Java 里基本都是虛函數,似乎也沒什麼不行的。
—— 除了慢點,是沒什麼不行的。
該不該用虛函數取決於你要做什麼,和語言本身沒關係。
虛函數的調用性能,如果不是寫遊戲引擎這種全程靠黑科技的程序,真不用擔心……我剛用測試框架跑了benchmark,一百萬次正常函數調用和一百萬次虛函數調用,在release模式下,只相差4msCPU:Core I7 4790K操作系統:Win10 RedStone編譯器:MinGW 5.3.0單元測試框架:Qt 5.7 QtTest
應該是什麼時候需要,沒有需求就不要用虛函數。不用被繼承那虛函數沒用,不用多態那也沒用。只有用到多態的時候使用虛函數。另外基類的析構函數也需要使用析構函數,是因為多態的副作用引起。
各位大牛都說的很好了,如果想了解C++在編譯過程中更具體的情況,可以參考《深度探索C++對象模型》這本書,中文版翻譯的也很好。
你說的沒什麼不行,是在對程序運行效率要求不高的情況下。像遊戲引擎中的核心部分,如果用Java,是可以卡出新高度的。
並不是說虛函數不好,而是一旦動用virtual,就牽扯到繼承,話說,你真的需要繼承嗎,這就要三思而行了
因為非虛函數都可以改成這樣實現:class A {void f();//改成下面這樣static void f(A a);}然後調用時候:A a;
a.f()改成a.f(a)
C++同時提供非虛和虛函數大概是考慮到非虛函數的使用率也是挺高的,如果都用static來搞又麻煩所謂好刀要用在刀刃上。
一般在用到運行時多態的地方可以用虛函數。虛函數會增加時間成本和空間成本。
關於虛函數的空間成本:虛表指針和虛函數表。這個可以參考《深度探索C++對象模型》,也可以參考我的CSDN博客專欄:C++對象模型的那些事兒之一:對象模型(上)和C++對象模型的那些事兒之二:對象模型(下)
時間成本的話:虛函數查表,以及用到了函數指針,性能必然會下降。這一方面可能只是幾個機器指令的開銷,但是積少成多,多少還是有一些影響。
最常見的需要使用虛函數的就是虛析構函數,效果很不錯。不應該用虛函數的場景只能說,你不需要運行時多態就不需要用虛函數。因為虛函數需要通過虛函數表間接調用,這就至少多了一個指針索引動作(先由對象內置的虛函數表指針找到該類的虛函數表,然後再調用對應成員函數),會稍微降低執行速度。
如果完全沒有虛函數,那麼C++就不會在類對象中生成虛函數表指針。這可以略微減小該類對象的大小。如果這類小對象極多,那麼每個對象少佔用幾個位元組,節省的空間還是非常可觀的。
綜上,為了能提供和C一樣的性能,C++允許在類中聲明非虛函數。
但,非虛函數沒辦法多態。所以,不允許在子類中改寫非虛函數(不是語法錯誤,但改寫後調用規則非常複雜,極易出錯,又沒有什麼明顯好處。所以幾乎任何公司的編碼規範都會禁止你這樣做)。看看虛函數的實現就知道了!記得陳皓老師(想了想還是加上老師吧 )寫過兩篇關於虛函數的,通俗易懂,可以網上搜索了瞅瞅!當然,有時間的話可以瞅瞅『深度探索c++對象模型』-侯俊傑老師譯
題主一律使用虛函數吧
為什麼:
1、能問出這種問題說明題主水平不高2、虛函數對性能有影響,但是在現在的硬體條件下可以忽略不計,除非做操作系統級別的工程,或者遊戲引擎核心代碼等3.水平不高的人短時間不會接觸到這個東西題主你想多了,用吧,我寫了7年程序都沒因為虛函數出過性能問題,可能一輩子都不會有了。
在不需要多態的時候
虛函數的代價算很小了,因為thiscall有針對優化,某些編譯器甚至比函數指針快好多。但當你不想讓某個方法有多態表現時,或者對某個方法是否該有多態表現沒想法的時候,都不要用虛函數。當且僅當你已經決定了加一個間接層次,再加virtual。這首先不是個效率問題,是準確表達設計思路的問題
C++中任何時候都不應該使用虛成員函數,除非你有一個好的理由。
先說問題中,前題錯誤。Java中所有private方法都不是virtual,靜態方法當然也不是,由final修飾的方法也不是。因此,虛方法沒有那麼多。
Java是單繼承,一個類只會有一個父類,比較容易按父類的要求實現虛函數,參考java.utils.Abstract*這些類,這些類是設計來用於繼承。但是對於設計並不是繼承的,比如FileInputStream,很難做到與父類一致。MFC中,對消息循環的機制不用虛函數而用宏批量寫類信息和添加類節點。因為如果用了虛函數,對於一個子類的函數回調來說成本太高,那個虛函數表也是無法直視了
保守的說:1. 需要不斷增加新方法的介面類
虛表的通常實現按順序查找虛函數,如果在中間添加方法會造成新舊版本不兼容
2. 需要跨編譯器使用的介面類不同編譯器,不同版本的編譯器的虛函數實現方式不一定相同3. 需要頻繁使用的短小函數,且關心性能如果是瓶頸,能省則省4. 不需要通過虛指針進行內存泄漏調查的地方if (性能優先) {
// 非動態綁定的虛函數調用會被編譯器轉換為靜態綁定, 沒有任何性能損失.
if (性能分析結果表明使用虛函數會對性能造成顯著影響) {
不使用虛函數;
} else {
解決完性能瓶頸再回來考慮這個問題;
}
} else {
使用虛函數;
}
我的粗淺的理解如下:
1. 如果一個類在將來可能被繼承
2. 它的某個方法希望表現出多態那麼該方法就是虛的,一個特殊的例子是,析構函數必須是虛的,否則在多態下,對象無法被正確析構。
effective c++ 第35條
虛函數的實現是有代價的,當不需要多態行為時,沒必要為此付出代價。
把c++當作c語言使用的時候不需要使用虛函數
推薦閱讀:
※C++ 一個 class 裡面全部是靜態函數,這種設計目的是什麼?
※默認的逐成員初始化和逐位複製有什麼不同?
※請大神看我對虛函數表和虛基類表的理解對不對?
※C++ 有哪些經常用到的設計模式?