C++ template —— 動多態與靜多態(六)

轉載:http://www.cnblogs.com/yyxt/p/5157517.html

前面的幾篇博文介紹了模板的基礎知識,并且也深入的講解了模板的特性。接下來的博文中,將會針對模板與設計進行相關的介紹。
------------------------------------------------------------------------------------------------------------
與傳統的語言構造相比,模板的不同之處在于:它允許我們在代碼中對類型和函數進行參數化。把(1)局部特化和(2)遞歸實例化組合起來,將會產生強大威力。接下來的幾篇博文,我們通過下面的一些設計技術來展示這些強大威力:
(1)泛型編程
(2)trait
(3)policy class?
(4)metaprogramming
(5)表達式模板

------------------------------------------------------------------------------------------------------------
第14章 模板的多態威力
多態是一種能夠令單一的泛型標記關聯不同特定行為的能力。對面向對象的程序設計范例而言,多態可以說是一塊基石。在C++中,這塊基石主要是通過繼承和虛函數來實現的。由于這兩個機制(繼承和虛函數)都是(至少一部分)在運行期進行處理的,因此我們把這種多態稱為動多態;我們平常所談論的C++多態指的就是這種動多態。然而,模板也允許我們使用單一的泛型標記,來關聯不同的特定行為;但這種(借助于模板的)關聯是在編譯期進行處理的,因此我們把這種(借助于模板的)多態稱為靜多態,從而和上面的動多態區分開來。
------------------------------------------------------------------------------------------------------------
14.1 動多態

使用繼承和虛函數,在這種情況下,多態的設計思想主要在于:對于幾個相關對象的類型,確定它們之間的一個共同功能集;然后在基類中,把這些共同的功能聲明為多個虛函數接口。每個具體類都派生自基類,生成了具體對象之后,客戶端代碼就可以通過指向基類的引用或指針來操作這些對象,并且能夠通過這些引用或者指針來實現虛函數的調度機制。也就是說,利用一個指向基類(子對象)的指針或者引用來調用虛成員函數,實際上將可以調用(指針或者引用實際上所代表的)具體類對象的相應成員。這種動多態是C++程序設計里面最常見的,這里不過多的闡述。

14.2 靜多態 模板也能夠被用于實現多態。如下例子:

復制代碼
// poly/statichier.hpp
#include "coord.hpp"// 具體的幾何對象類Circle
// - 并沒有派生自任何其他的類
class Circle
{public:void draw() const;Coord center_of_gravity() const;...
};// 具體的幾何對象類Line
// - 并沒有派生自任何其他的類
class Line
{public:void draw() const;Coord center_of_gravity() const;...
};
....
復制代碼

現在,使用這些類的應用程序看起來如下所示:

復制代碼
// poly/staticpoly.cpp
#include "statichier.hpp"
#include <vector>// 畫出任意GeoObj
// method2
template <typename GeoObj>
void myDraw(GeoObj const& obj)     // GeoObj是模板參數
{obj.draw();      // 根據對象的類型調用相應的draw()
}
......int main()
{Line l;Circle c;myDraw(l);         // myDraw<Line>(GeoObj&) => Line::draw()myDraw(c);         // myDraw<Circle>(GeoObj&) => Circle::draw()
}// method1:如果使用動多態,myDraw函數會是如下形式:
void myDraw(GeoObj const& obj)      // GeoObj是一個抽象基類
{obj.draw();
}
復制代碼

通過比較myDraw()的這兩個實現,我們可以看出:主要的區別在于method2的GeoObj的規范是模板參數,而不是一個公共基類。然而,在這個現象的背后,還存在更多本質的差別。例如,使用動多態(method1),我們在運行期只具有一個myDraw()函數,而如果使用模板,我們則可能具有多個不同的函數,諸如myDraw<Line>()和myDraw<Circle>()。

14.3 動多態和靜多態

我們來對多態進行分類,并對這兩種多態進行比較。

14.3.1 術語

動多態和靜多態為不同的C++編程idioms提供了支持:
(1)通過繼承實現的多態是綁定的和動態的:
1. 綁定的含義是:對于參與多態行為的類型,它們(具有多態行為)的接口是在公共基類的設計中就預先確定的(有時候也把綁定這個概念稱為入侵的或者插入的)。
2. 多態的含義是:接口的綁定是在運行期(動態)完成的。
(2)通過模板實現的多態是非綁定的和靜態的:
1. 非綁定的含義是:對于參與多態行為的類型,它們的接口是沒有預先確定的(有時也稱這個概念為非入侵的或者非插入的)。
2. 靜態的含義是:接口的綁定是在編譯期(靜態)完成的。

14.3.2 優點和缺點
(1)C++的動多態具有下列優點:
1. 能夠優雅地處理異類集合;
2. 可執行代碼的大小通常比較小(因為只需要一個多態函數,但對于靜多態而言,為了處理不同的類型,必須生成多個不同的模板實例);
3. 可以對代碼進行完全編譯;因此并不需要發布實現源碼(但是,分布模板庫通常都需要同時發布模板實現的源代碼);
(2)另一方面,C++靜多態則具有下列優點:

1. 可以很容易地實現內建類型的集合。更廣義地說,并不需要通過公共基類來表達接口的共同性;

2. 所生成的代碼效率通常都比較高(因為并不存在通過指針的間接調用,而且,可以進行演繹的非虛擬函數具有更多的內聯機會);

3. 對于只提供部分接口的具體類型,如果在應用程序中只是使用到這一部分接口,那么也可以使用該具體類型,而不必在乎該類型是否提供其他部分的接口。

通常而言,與動多態相比,靜多態被認為具有更好的類型安全性:因為靜多態在編譯期會對所有的綁定操作進行檢查。例如,假設我們嘗試把一個錯誤類型的對象插入到一個容器中,如果這個容器是根據模板實例化而生產的話,那么幾乎不會有危險,因為在編譯期就可以檢查出這個錯誤;但如果該容器所期望的元素是指向公共基類的指針,那么這些指針最后很有可能會指向不同類型的完整對象,而這就有可能會插入錯誤類型的對象。

在實際應用中,對于看起來相同的接口,如果在它們背后隱藏著一些語義假設的話,那么模板實例化體(靜多態)有時也會導致一些問題。例如,對于一個假設具有關聯運算符 + 的模板,如果基于一個沒有關聯該運算符的類型來實例化這個模板,那么就會出現一些問題。然而,基于繼承體系的多態則很少會出現這種語義非匹配的問題,因為公共接口規范已經在基類中(更加)顯式地指定了。

14.3.3 組合這兩種多態

顯然,你可以組合這兩種形式的多態。例如,你可以從一個公共基類派生出不同種類的幾何對象類,從而能夠處理屬于異類集合的不同幾何對象。另一方面,你仍然可以使用模板來編寫針對某種幾何對象的代碼。

在后面的博文xxxx中將進一步闡述繼承和模板的組合。在第16章中,我們將看到:如何對成員函數的虛擬性進行參數化;當使用基于繼承的奇異遞歸模板模式的時候,靜多態要犧牲哪些額外的靈活性。

14.4 新形式的設計模板

這種新形式的靜多態帶來了實現設計模式的新方法。例如,以在C++程序設計中扮演重要角色的橋模式為例。我們使用橋模式的目的是為了能夠在同一接口的多個不同實例中進行切換。我們通常可以使用一個指針來引用具體的實現,然后把所有的調用都委托給這個(包含具體實現的)類,從而達到我們的目的(見圖14.3)

?

然而,如果實現的類型在編譯期就已經是確定的,那么我們就可以借助于模板的方法來實現橋模式(見圖14.4)。這將可以帶來更好的類型安全性,并且也能避免使用指針,而且還能帶來更高的效率。

?

14.5 泛型程序設計
到目前為止,在C++泛型程序設計領域中,最顯著的貢獻就是STL(Standard Template Library),它后來被采納并引入到C++標準庫中。STL借助于迭代器對操作進行了參數化,從而避免了操作定義在數量上的過度膨脹。在此,你并不需要為每個容器都實現每一個操作,只需要實現某個算法一次,就可以把該算法應用到每個容器中。換句話說,泛型程序設計的“粘合劑”就是:由容器提供的并且能被算法所使用的迭代器。迭代器之所以能夠肩負這樣的任務,是由于容器為迭代器提供了一些特定的接口,而算法所使用的正是這些接口。我們通常也把每個這樣的接口稱為一個concept(即約束),它說明一個模板(即容器)如果要并入這個框架(即STL),就必須履行或者實現這些約束(也即,符合STL框架標準)。
從原則上講,也可以使用動多態來實現這些類似于STL的功能。然而,用動多態實現的功能使用起來肯定會很受限制,因為與迭代器的概念相比,動多態的虛函數調用機制將會是一種重量級的實現機制,這就會對效率產生很大的影響。譬如增加一層基于虛函數的接口層,通常就會影響操作的效率,而且這種影響的程度可能是幾個數量級的(甚至更加嚴重)。
事實上,泛型程序設計是相當實用的,因為它所依賴的是靜多態,而靜多態會要求在編譯期對接口進行解析。另一方面,這種要求(即對接口在編譯期進行解析)還會帶來一些與面向對象程序設計原則截然不同的新設計原則。

分類:?C++ Template

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/384340.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/384340.shtml
英文地址,請注明出處:http://en.pswp.cn/news/384340.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

變量之間的區別

全局變量、局部變量、靜態全局變量、靜態局部變量的區別 c變量根據定義具有不同的生命周期&#xff0c;會有不同的作用域&#xff0c;主要有六個作用域&#xff1a;全局作用域&#xff0c;局部作用域&#xff0c;文件作用域&#xff0c;類作用域&#xff0c;語句作用域&#xf…

計算機的網絡體系以及參考模型

計算機的網絡體系以及參考模型一、OSI七層模型二、TCP/IP參考模型三、TCP/IP 五層參考模型四、OSI 模型和 TCP/IP 模型異同比較五、OSI 和 TCP/IP 協議之間的對應關系六、為什么 TCP/IP 去除了表示層和會話層&#xff1f;七、數據如何在各層之間傳輸&#xff08;數據的封裝過程…

C++ 模板詳解(二)

轉載&#xff1a;http://www.cnblogs.com/gw811/archive/2012/10/25/2736224.html 四、類模板的默認模板類型形參 1、可以為類模板的類型形參提供默認值&#xff0c;但不能為函數模板的類型形參提供默認值。函數模板和類模板都可以為模板的非類型形參提供默認值。 2、類模板的類…

c++類對象的創建方式

對象創建限制在堆或棧 c類對象的創建方式對象創建限制在堆或棧C 中的類的對象的建立模式如何將類限制在堆上呢&#xff1f;C 中的類的對象的建立模式 C 中的類的對象的建立模式分為兩張&#xff1a;靜態建立&#xff0c;動態建立 靜態建立&#xff1a;由編譯器為對象在棧空間…

C++ 模板詳解(一)

轉載&#xff1a;http://www.cnblogs.com/gw811/archive/2012/10/25/2738929.html C模板 模板是C支持參數化多態的工具&#xff0c;使用模板可以使用戶為類或者函數聲明一種一般模式&#xff0c;使得類中的某些數據成員或者成員函數的參數、返回值取得任意類型。 模板是一種對類…

劍指Offer09. 用兩個棧實現隊列

class CQueue { public:stack<int> stack1,stack2;CQueue() {//初始化棧while(!stack1.empty()){stack1.pop();}while(!stack2.empty()){stack2.pop();}}void appendTail(int value) {stack1.push(value);}int deleteHead() {if(stack2.empty()){while(!stack1.empty()){…

rk3588 之啟動

目錄 uboot版本配置修改編譯 linux版本配置修改編譯 啟動sd卡啟動制作spi 燒錄 參考 uboot 版本 v2024.01-rc2 https://github.com/u-boot/u-boot https://github.com/rockchip-linux/rkbin 配置修改 使用這兩個配置即可&#xff1a; orangepi-5-plus-rk3588_defconfig r…

C++引用詳解

轉載&#xff1a;http://www.cnblogs.com/gw811/archive/2012/10/20/2732687.html 引用的概念 引用&#xff1a;就是某一變量&#xff08;目標&#xff09;的一個別名&#xff0c;對引用的操作與對變量直接操作完全一樣。 引用的聲明方法&#xff1a;類型標識符 &引用名目標…

劍指Offer03.數組中重復的數字

找出數組中重復的數字。 在一個長度為 n 的數組 nums 里的所有數字都在 0&#xff5e;n-1 的范圍內。數組中某些數字是重復的&#xff0c;但不知道有幾個數字重復了&#xff0c;也不知道每個數字重復了幾次。請找出數組中任意一個重復的數字。 示例 1&#xff1a; 輸入&…

C++ 模板全特化中的函數特化

轉載&#xff1a;http://blog.csdn.net/rain_qingtian/article/details/15815251 [cpp] view plaincopy print?#include <iostream> using namespace std; template<typename T> bool isLess(T x, T y) { cout << "general version\n&q…

c++面向對象總結

c面向對象總結什么是面向對象&#xff1f;面向對象的三大特性重寫和重載的區別隱藏和重寫&#xff0c;重載的區別什么是多態&#xff1f;多態如何實現什么是面向對象&#xff1f;面向對象的三大特性 面向對象&#xff1a;對象是指具體的某一個事物&#xff0c;這些事物的抽象就…

類模板static成員的使用

轉載&#xff1a;http://blog.csdn.net/ljq32/article/details/7911390 1. 與普通類的static成員一樣&#xff0c;類內部聲明一次&#xff0c;類外部定義一次&#xff0c;定義時可以設置也可以不設置初始值; 2. 類模板內部聲明與普通類的static成員一致&#xff1a; [html] vi…

Linux網絡編程服務器模型選擇之循環服務器

轉載&#xff1a;http://www.cnblogs.com/lizhenghn/p/3617608.html 在網絡程序里面&#xff0c;通常都是一個服務器處理多個客戶機&#xff0c;為了出個多個客戶機的請求&#xff0c;服務器端的程序有不同的處理方式。本節開始介紹Linux下套接字編程的服務器模型選擇&#xff…

劍指Offer04. 二維數組中的查找

在一個 n * m 的二維數組中&#xff0c;每一行都按照從左到右遞增的順序排序&#xff0c;每一列都按照從上到下遞增的順序排序。請完成一個高效的函數&#xff0c;輸入這樣的一個二維數組和一個整數&#xff0c;判斷數組中是否含有該整數。 相當于二叉搜索樹,左孩子比根節點小&…

Linux網絡編程服務器模型選擇之并發服務器(上)

轉載&#xff1a;http://www.cnblogs.com/lizhenghn/p/3617666.html 與循環服務器的串行處理不同&#xff0c;并發服務器對服務請求并發處理。循環服務器只能夠一個一個的處理客戶端的請求&#xff0c;顯然效率很低。并發服務器通過建立多個子進程來實現對請求的并發處理。并發…

劍指Offer10- II. 青蛙跳臺階問題

一只青蛙一次可以跳上1級臺階&#xff0c;也可以跳上2級臺階。求該青蛙跳上一個 n 級的臺階總共有多少種跳法。 答案需要取模 1e97&#xff08;1000000007&#xff09;&#xff0c;如計算初始結果為&#xff1a;1000000008&#xff0c;請返回 1。 示例 1&#xff1a; 輸入&a…

Linux網絡編程服務器模型選擇之并發服務器(下)

轉載&#xff1a;http://www.cnblogs.com/lizhenghn/p/3618986.html 前面兩篇文章&#xff08;參見&#xff09;分別介紹了循環服務器和簡單的并發服務器網絡模型&#xff0c;我們已經知道循環服務器模型效率較低&#xff0c;同一時刻只能為一個客戶端提供服務&#xff0c;而且…

劍指Offer05. 替換空格

請實現一個函數&#xff0c;把字符串 s 中的每個空格替換成"%20"。 示例 1&#xff1a; 輸入&#xff1a;s “We are happy.” 輸出&#xff1a;“We%20are%20happy.” class Solution { public:string replaceSpace(string s) {int count0;int lens.size();stri…

Linux網絡編程服務器模型選擇之IO復用循環并發服務器

轉載&#xff1a;http://www.cnblogs.com/lizhenghn/p/3619091.html 在前面我們介紹了循環服務器&#xff0c;并發服務器模型。簡單的循環服務器每次只能處理一個請求&#xff0c;即處理的請求是串行的&#xff0c;效率過低&#xff1b;并發服務器可以通過創建多個進程或者是線…

數據結構(六)二叉樹的遍歷(遞歸非遞歸方法)

數據結構&#xff08;六&#xff09;二叉樹的遍歷&#xff08;遞歸非遞歸方法&#xff09; 一、遞歸方法 1.先序遍歷 void PreOrder(BiTree T) {visit(T);PreOrder(T->LChild)PreOrder(T->RChild) }2.先序遍歷 void PreOrder(BiTree T) {PreOrder(T->LChild)visit…