C++編譯期多型與執行期多型
前言
今日的C++不再是個單純的“帶類的C”語言,它已經發展成為一個多種次語言所組成的語言集合,其中泛型程式設計與基於它的STL是C++發展中最為出彩的那部分。在面向物件C++程式設計中,多型是OO三大特性之一,這種多型稱為執行期多型,也稱為動態多型;在泛型程式設計中,多型基於template(模板)的具現化與函式的過載解析,這種多型在編譯期進行,因此稱為編譯期多型或靜態多型。在本文中,我們將瞭解:
- 什麼是執行期多型
- 什麼是編譯期多型
- 它們的優缺點在哪
執行期多型
執行期多型的設計思想要歸結到類繼承體系的設計上去。對於有相關功能的物件集合,我們總希望能夠抽象出它們共有的功能集合,在基類中將這些功能宣告為虛介面(虛擬函式),然後由子類繼承基類去重寫這些虛介面,以實現子類特有的具體功能。典型地我們會舉下面這個例子:
class Animal { public : virtual void shout() = 0; }; class Dog :public Animal { public: virtual void shout(){ cout << "汪汪!"<<endl; } }; class Cat :public Animal { public: virtual void shout(){ cout << "喵喵~"<<endl; } }; class Bird : public Animal { public: virtual void shout(){ cout << "嘰喳!"<<endl; } }; int main() { Animal * anim1 = new Dog; Animal * anim2 = new Cat; Animal * anim3 = new Bird; //藉由指標(或引用)呼叫的介面,在執行期確定指標(或引用)所指物件的真正型別,呼叫該型別對應的介面 anim1->shout(); anim2->shout(); anim3->shout(); //delete 物件 ... return 0; }
執行期多型的實現依賴於虛擬函式機制。當某個類聲明瞭虛擬函式時,編譯器將為該類物件安插一個虛擬函式表指標,併為該類設定一張唯一的虛擬函式表,虛擬函式表中存放的是該類虛擬函式地址。執行期間通過虛擬函式表指標與虛擬函式表去確定該類虛擬函式的真正實現。
執行期多型的優勢還在於它使處理異質物件集合稱為可能:
//我們有個動物園,裡面有一堆動物 int main() { vector<Animal*>anims; Animal * anim1 = new Dog; Animal * anim2 = new Cat; Animal * anim3 = new Bird; Animal * anim4 = new Dog; Animal * anim5 = new Cat; Animal * anim6 = new Bird; //處理異質類集合 anims.push_back(anim1); anims.push_back(anim2); anims.push_back(anim3); anims.push_back(anim4); anims.push_back(anim5); anims.push_back(anim6); for (auto & i : anims) { i->shout(); } //delete物件 //... return 0; }
總結:執行期多型通過虛擬函式發生於執行期
編譯期多型
對模板引數而言,多型是通過模板具現化和函式過載解析實現的。以不同的模板引數具現化導致呼叫不同的函式,這就是所謂的編譯期多型。
相比較於執行期多型,實現編譯期多型的類之間並不需要成為一個繼承體系,它們之間可以沒有什麼關係,但約束是它們都有相同的隱式介面。我們將上面的例子改寫為:
class Animal
{
public :
void shout() { cout << "發出動物的叫聲" << endl; };
};
class Dog
{
public:
void shout(){ cout << "汪汪!"<<endl; }
};
class Cat
{
public:
void shout(){ cout << "喵喵~"<<endl; }
};
class Bird
{
public:
void shout(){ cout << "嘰喳!"<<endl; }
};
template <typename T>
void animalShout(T & t)
{
t.shout();
}
int main()
{
Animal anim;
Dog dog;
Cat cat;
Bird bird;
animalShout(anim);
animalShout(dog);
animalShout(cat);
animalShout(bird);
getchar();
}
在編譯之前,函式模板中t.shout()呼叫的是哪個介面並不確定。在編譯期間,編譯器推斷出模板引數,因此確定呼叫的shout是哪個具體型別的介面。不同的推斷結果呼叫不同的函式,這就是編譯器多型。這類似於過載函式在編譯器進行推導,以確定哪一個函式被呼叫。
執行期多型與編譯期多型優缺點分析
執行期多型優點
- OO設計中重要的特性,對客觀世界直覺認識。
- 能夠處理同一個繼承體系下的異質類集合。
執行期多型缺點
- 執行期間進行虛擬函式繫結,提高了程式執行開銷。
- 龐大的類繼承層次,對介面的修改易影響類繼承層次。
- 由於虛擬函式在執行期在確定,所以編譯器無法對虛擬函式進行優化。
虛表指標增大了物件體積,類也多了一張虛擬函式表,當然,這是理所應當值得付出的資源消耗,列為缺點有點勉強。
編譯期多型優點
- 它帶來了泛型程式設計的概念,使得C++擁有泛型程式設計與STL這樣的強大武器。
- 在編譯器完成多型,提高執行期效率。
具有很強的適配性與鬆耦合性,對於特殊型別可由模板偏特化、全特化來處理。
編譯期多型缺點
- 程式可讀性降低,程式碼除錯帶來困難。
- 無法實現模板的分離編譯,當工程很大時,編譯時間不可小覷。
- 無法處理異質物件集合。
關於顯式介面與隱式介面
所謂的顯式介面是指類繼承層次中定義的介面或是某個具體類提供的介面,總而言之,我們能夠在原始碼中找到這個介面.顯式介面以函式簽名為中心,例如
void AnimalShot(Animal & anim)
{
anim.shout();
}
我們稱shout為一個顯式介面。在執行期多型中的介面皆為顯式介面。
而對模板引數而言,介面是隱式的,奠基於有效表示式。例如:
template <typename T>
void AnimalShot(T & anim)
{
anim.shout();
}
對於anim來說,必須支援哪一種介面,要由模板引數執行於anim身上的操作來決定,在上面這個例子中,T必須支援shout()操作,那麼shout就是T的一個隱式介面。
相關推薦
C++編譯期多型與執行期多型
前言 今日的C++不再是個單純的“帶類的C”語言,它已經發展成為一個多種次語言所組成的語言集合,其中泛型程式設計與基於它的STL是C++發展中最為出彩的那部分。在面向物件C++程式設計中,多型是OO三大特性之一,這種多型稱為執行期多型,也稱為動態多型;在泛型程式設計中,多型基於template(模板)的具現化
c++ 編譯期與執行期總結
一 見識編譯期的力量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include "stdafx.h" #include
C++編譯期函式/變數檢測技術,模擬VC關鍵字__if_exists
轉帖請註明出處 http://www.cppblog.com/cexer/archive/2008/07/06/55484.html VC當中有一個鮮為人知的關鍵字,除了微軟自己的程式碼,我從未在任何地方看到有人用過它。雖然它的功能很強大,不過除非設計上的問題或是一些無法排除的困難,否則幾乎從不會需要用到它
Windows10下WebAssembly C/C++編譯環境的搭建與Hello World嘗試
ref 5.0 har 新版 找不到 this www 完整 編譯環境 首先,不論是在Windows、Linux還是Mac上,Webassembly的編譯都是主要依賴於Emscripten SDK這個工具的。但是,在這裏必須要吐槽一下,不論是WebAssembly官網、We
值類型與引用類型的區別
希望 false 浮點 引號 博客 長度 導致 忘記 基礎 值類型 基本類型 4類8種 整型 byte 字節型 1長度 0~255
Java基本數據類型與包裝類型(轉)
最大值 lib short static false boolean 面向對象 val int操作 基本數據類型的包裝類 為什麽需要包裝類? Java並不是純面向對象的語言。Java語言是一個面向對象的語言,但是Java的基本數據類型卻不是面向對象的。但是我們在實際使
整型與布爾型的轉換
scan sca std turn div tdi logs cnblogs () 描述 將一個整型變量的值賦給一個布爾型變量,再將這個布爾型變量的值賦給一個整型變量,得到的值是多少? 輸入一個整型範圍內的整數,即初始時整型變量的值。輸出一個整數,經過上述過程後得到的結果。
引用類型與原始類型的區別
bject alert for strong https 說明 .com name 3.6 js基本數據類型: js基本數據類型包括:undefined,null,number,boolean,string.基本數據類型是按值訪問的,就是說我們可以操作保存在變量中的實際的值
jdbc類型與java類型
add ger ase time com tin alt varchar username <resultMap id="BaseResultMap" type="com.bjsxt.portl.pojo.User" > <id column=
值類型與引用類型
val 高精度 type inter 派生 obj gate 精度 枚舉 區別: 1:在存儲區上的區別: 值類型是存儲在棧上面的,引用類型是存儲在堆上面的。 2:在賦值上,值類型是直接賦新值給變量,但是引用類型是開辟一塊新的空間來存放新值,並將之前指向舊的值的那個地址改成指
59.關系型與document類型數據模型對比
存儲 span 一個 對象 des 一起 _id name 外鍵關聯 現假設有如下兩個類: class Department(object): def __init__(self, dept_id, name, desc, employees=[]
Number類型與String類型之間的轉換
它的 cts IT 深入 spa 字面值 但是 parseint word 深入了解 Number 類型 Number 類型作為 JS 的基本數據類型之一,被應用在程序中的各種場景,其重要性就如數字對於我們日常生活。 下面就讓我們來一起深入了解下,為以後的“策馬奔騰”做好鋪
Char類型與Sting類型的數字字符轉換時的不同點
不同 ascii碼 區分 引號 string類型 轉換 char con 類型轉換 這是在一次編程時的bug裏偶然發現的一個問題。在C#中,單引號默認是char類型字符,而雙引號默認是string類型字符。對於char類型的數字字符,通過強制類型轉換或者convert轉
Swift 中的值類型與引用類型
復合 內容 淺拷貝 swift 行為 類型 引用 改變 修飾 頂級修飾 次級修飾 賦值類型 存儲類型 值類型 值類型 深拷貝 棧 值類型 引用類型 淺拷貝 堆 引用類型 值類型 淺拷貝 堆 引用類型 引用類型 淺拷貝 堆
ssm學習筆記——數組類型與List類型綁定
detail [] req clas orm 綁定 cti hide ucc 一:數組類型 1.實現 1)前端 <form action="${pageContext.request.contextPath }/getlist.action" meth
js一元操作符之遞增遞減操作符的前置型與後置型詳解
javascrip中,一元操作符中 遞增操作符: ++ 遞減操作符: -- 這兩個操作符可以放在變數的前面,也可以放在變數的後面。但是前後是有區別的。 1. a++與++a都是對a進行自加1,結果完全相同;遞減操作符都是自減1。
枚舉類型與集合類型
maria 集合類 table num con mar lec ble sel 枚舉類型與集合類型 字段的值只能在給定範圍中選擇,如單選框,多選框 enum 單選 只能在給定的範圍內選一個值,如性別 sex 男male/女female set 多選 在給定的範圍內可以選擇一
java基本類型與引用類型
float value 賦值 jdk1.5 解釋 eval 查看 因此 常見 目錄java基本類型與引用類型目錄一基本數據類型二引用類型三基本類型與引用類型的區別默認值內存分配自動裝箱自動拆箱自動裝箱拆箱帶來的問題程序的性能空指針異常對象相等比較時一、基本數據類型java中
RabbitMQ與.net core(五) topic類型 與 headers類型 的Exchange
模糊 collect ref lar create etc ade del system 原文:RabbitMQ與.net core(五) topic類型 與 headers類型 的Exchange1.topic類型的Exchange 我們之前說過Topic類型的Exch
深入理解計算機系統之整型與浮點型
在計算機儲存系統裡面,算術型別可以分為兩類:整型(intergral type,包括字元和布林型別在內)和浮點型。在看簡單地看了深入理解計算機系統的第二章後,有了稍微深刻但是有非常淺顯的理解,然後又看了阮師兄的一篇博文,所以做了一點筆記。 下面先來看一個例子程