解析C++中的虛擬函數及其靜態類型和靜態類型。本站提示廣大學習愛好者:(解析C++中的虛擬函數及其靜態類型和靜態類型)文章只能為提供參考,不一定能成為您想要的結果。以下是解析C++中的虛擬函數及其靜態類型和靜態類型正文
虛擬函數是C++說話引入的一個很主要的特征,它供給了“靜態綁定”機制,恰是這一機制使得繼續的語義變得絕對了了。
(1)基類籠統了通用的數據及操作,就數據而言,假如該數據成員在各派生類中都須要用到,那末就須要將其聲明在基類中;就操作而言,假如該操尴尬刁難各派生類都成心義,不管其語義能否會被修正或擴大,那末就須要將其聲明在基類中。
(2)有些操作,假如關於各個派生類而言,語義堅持完整分歧,而無需修正或擴大,那末這些操出聲明為基類的非虛擬成員函數。各派生類在聲明為基類的派生類時,默許繼續了這些非虛擬成員函數的聲明/完成,好像默許繼續基類的數據成員一樣,而不用別的做任何聲明,這就是繼續帶來的代碼重用的長處。
(3)別的還有一些操作,固然關於各派生類而言都成心義,然則其語義其實不雷同。這時候,這些操作應當聲明為基類的虛擬成員函數。各派生類固然也默許繼續了這些虛擬成員函數的聲明/完成,然則語義上它們應當對這些虛擬成員函數的完成停止修正或許擴大。別的在完成這些修正或擴大進程中,須要用到額定的該派生類獨有的數據時,將這些數據聲明為此派生類本身的數據成員。
再斟酌更年夜配景下的繼續系統,當更高條理的法式框架(繼續系統的應用者)應用此繼續系統時,它處置的是一個籠統條理的對象聚集(即基類)。固然這個對象聚集的成員本質上能夠是各類派生類對象,但在處置這個對象聚集中的對象時,它用的是籠統條理的操作。其實不辨別在這些操作中,哪些操尴尬刁難各派生類來講是堅持不變的,而哪些操尴尬刁難各派生類來講有所分歧。這是由於,當運轉時現實履行到各操作時,運轉時體系可以或許辨認哪些操作須要用到“靜態綁定”,從而找到對應此派生類的修正或擴大的該操作版本。
也就是說,即只需關懷它本身成績域的營業邏輯,只需包管准確,其義務就算完成了
。即便繼續系統外部增長了某種派生類,或許刪除某種派生類,或許某某派生類的某個虛擬函數的完成產生了轉變,它的代碼不用任何修正。這也意味著,法式的模塊化水平獲得了極年夜的進步。而模塊化的進步也就意味著可擴大性、可保護性,和代碼的可讀性的進步,這也是“面向對象”編程的一個很年夜的長處。
虛擬函數的靜態類型和靜態類型
先來看一個成績,假如一個子類重載的虛擬函數為privete,那末經由過程父類的指針可以拜訪到它嗎?
#include <IOSTREAM> class B { public: virtual void fun() { std::cout << "base fun called"; }; }; class D : public B { private: virtual void fun() { std::cout << "driver fun called"; }; }; int main(int argc, char* argv[]) { B* p = new D(); p->fun(); return 0; }
運轉時會輸入
driver fun called
從這個試驗,可以更深刻的懂得虛擬函數編譯時的一些特點:
在編譯虛擬函數挪用的時刻,例如p->fun(); 只是按其靜態類型來處置的, 在這裡p的類型就是B,不會斟酌其現實指向的類型(靜態類型)。
也就是說,碰著p->fun();編譯器就看成挪用B的fun來停止響應的檢討和處置。
由於在B裡fun是public的,所以這裡在“拜訪掌握檢討”這一關就完整可以經由過程了。然後就會轉換成(*p->vptr[1])(p)如許的方法處置, p現實指向的靜態類型是D,所以p作為參數傳給fun後(類的非靜態成員函數都邑編譯加一個指針參數,指向挪用該函數的對象,我們平凡用的this就是該指針的值), 現實運轉時p->vptr[1]則獲得到的是D::fun()的地址,也就挪用了該函數, 這也就是靜態運轉的機理。
為了進一步的試驗,可以將B裡的fun改成private的,D裡的改成public的,則編譯就會失足。
C++的留意條目中有一條" 毫不從新界說繼續而來的缺省參數值" (Effective C++ Item37, never redefine a function's inherited default parameter value) 也是異樣的事理。
可以再做個試驗
class B { public: virtual void fun(int i = 1) { std::cout << "base fun called, " << i; }; }; class D : public B { private: virtual void fun(int i = 2) { std::cout << "driver fun called, " << i; }; };
則運轉會輸入
driver fun called, 1
關於這一點,Effective上講的很清晰“virtual 函數系靜態綁定, 而缺省參數倒是靜態綁定”,也就是說在編譯的時刻曾經依照p的靜態類型處置其默許參數了,轉換成了(*p->vptr[1])(p, 1)如許的方法。