国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > C++ > 正文

解析C++中的虛擬函數及其靜態類型和動態類型

2020-05-23 14:02:28
字體:
來源:轉載
供稿:網友

虛擬函數是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)這樣的方式。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 津南区| 河南省| 四会市| 文昌市| 罗平县| 余庆县| 南漳县| 铁岭市| 白玉县| 塔城市| 中阳县| 乌拉特前旗| 莱西市| 九龙县| 延边| 武安市| 冀州市| 永平县| 平江县| 高安市| 房产| 宾川县| 新民市| 喜德县| 乌兰察布市| 志丹县| 读书| 布拖县| 巴青县| 金昌市| 蒙山县| 双牌县| 密山市| 通海县| 奉新县| 伊宁市| 霍林郭勒市| 宣汉县| 沙雅县| 肇州县| 定襄县|