為何C++/Rust都不允許靜態函數是虛的?

按理說,virtual函數與非virtual相比,區別只在於,這個函數是要運行時查找具體的入口地址再調用的,請不要根據編譯器類型信息就決定函數的入口地址。

按照傳統的虛表方法來實現這個語義,編譯的時候,我把一個靜態函數地址也塞到虛表裡面去,把被調用的函數名字翻譯成虛表的索引,運行時的時候查一下虛表不就行了?只要編譯時能確定虛表的位置以及把被調用函數名正確翻譯成虛表索引(哪張虛表+函數地址記錄的編號)就行了。

這樣不就可以做到根據一個對象的實際類型來調用實際的靜態函數了嗎?

trait object - 知乎專欄

這篇文章說靜態函數塞不進虛表,所以為啥塞不進啊?


你的定義不夠精確。否則的話你自己就能發現問題。

這個函數是要運行時查找具體的入口地址再調用的

其實應該是這個函數x是要運行時查找所在對象具體的入口地址再調用的。或者說,虛函數表是從所在對象索引的。

而static不存在所在對象。所以怎麼找它的虛函數表?別忘了在加入static之前,你需要調用一個靜態函數,得這樣:

static_cast&(0)-&>Func();

因為Func和對象無關,所以這裡搞個0也無所謂。後來有了靜態函數,你可以寫成:

T::Func();

所以,你這下明白了為什麼虛函數表不能放靜態函數了吧。

你非得要類似功能的話,不用static就行了唄,就把那個函數變成一個成員。那就隨便virtual了。


因為你調用靜態函數的方法是

類名::函數名(bitches)

你虛來幹嘛?類名反正都寫出來了。


網上有現成答案啊,還要我幫你搜索 https://stackoverflow.com/questions/1820477/c-static-virtual-members

簡單點總結就是這麼做沒什麼意義。你想達到什麼目的,有什麼應用場景?朝這方面多想想。


某些語言,類本身就是對象(而且是個singleton對象),而且數據成員全部是pimpl的,擁有運行時升級(或者編譯/鏈接後升級)的能力,那確實可以讓靜態函數虛起來,例如可以讓你在模塊A的時候以為在調用a方法,但是編寫完模塊B後a方法徹底被替換成b方法。

C++類只是個類型名字,只是個名字空間,那當然就沒有辦法提供任何動態能力咯。Rust我不清楚。


虛函數解決的問題是基類類型指針指向繼承類對象時,如何調用的問題,因為此時編譯器無法知道這個對象是什麼類型的。

而靜態函數不存在這個問題,只要調用,編譯器一定知道它是什麼類型。


也許有用,但用處太小.

就算加上這麼個功能,也是個陰暗的陷阱.

C++包袱夠多了,饒了他吧.

實在想用,把static去掉.


虛函數在更 OO 的語言中,比如 Java 叫做 abstract method。虛函數解決的是多態問題,而 static 函數是所有實例共享的東西,跟多態不沾邊


照你這麼說類不需要繼承了對吧?父類的靜態函數和子類的靜態函數怎麼分配索引,你要曉得,所有的虛函數在虛表中的索引是一樣的。


因為虛函數往往用作實現dynamic dispatch


我想應該不是virtual和非virtual的區別吧,靜態函數不與對象綁定,而虛函數表指針是在對象上的,一個靜態函數,我到底是傳對象地址進去還是不傳呢?


題主的最大問題出在對oo的理解上。

在oo里,靜態方法的概念就是指一個方法屬於一個類而不是一個對象。因此語法上靜態方法必須可以通過類名直接訪問,且語意上必須訪問的是這個類的,而不是其子類的。如果用delay binding 去實現靜態方法,那麼運行時調用到的就可能是子類的方法,這是違背了靜態方法原本的語意的。

在oo里,靜態這個概念本身就包含有「編譯期決定」的意義。而virtual的語意卻是運行時決定。如果靜態方法可以為virtual,那不是自相矛盾嗎?

不知道題主是為何想實現virtual static function 的。如果你是想通過對象,對象的指針或引用去訪問這個方法,那就不應該把這個方法設計成靜態方法。如果你想通過類名直接訪問,那正如前文所述,這樣的實現是在給自己留坑:你調用到的可能不是這個類里的方法。

題主你需要補充一下oo 方面的知識,不能僅僅是學習C++本身。學習任何一門面向對象編程語言都必須建立在對面向對象本身的概念有一定理解的基礎之上。


你覺得為什麼要實現這樣的功能呢?


你到底要靜態行為還是動態行為?


靜態函數不屬於任何對象所以不能有this指針,沒有this指針,哪來的虛表啊?


你自己也說了,根據「對象」的「實際類型」,來調用,問題類型不是對象,調用靜態函數的時候,類型確定了也不存在「實際類型」

再說你把靜態函數整成虛的,還不就是為了弄一個Laravel 里的Facade,動態的連媽都不認識了,還寫個毛線代碼


因為靜態函數裡面放虛函數表會很大程度導致內存不足然後引發CPU過熱,這樣就會有未定義行為。


推薦閱讀:

如何優化一個讀取命令並執行的程序?
C++的完美轉發只能針對形如T &&的形參嗎?
現在C++開發是不是都遵守C++11標準,Linux下的多線程編程是優先考慮C++11的線程庫,還是用系統線程API封裝?
大學C++應該怎麼學?
用很厚的教材學編程的時候,該如何一邊攤開書一邊敲代碼?

TAG:C | Rust編程語言 |