今天的C++已是個多重泛型編程語言(multiparadigm programming lauguage),一個同時支持過程形式(procedural)、面向對象形式(object-oriented)、函數形式(functional)、泛型形式(generic)、元編程形式(metaprogramming)的語言。 這些能力和彈性使C++成爲一個無可匹敵的工具,但也可能引起使用者的某些迷惑,好比多態。在這幾種編程泛型中,面向對象編程、泛型編程以及很新的元編程形式都支持多態的概念,但又有所不一樣。 C++支持多種形式的多態,從表現的形式來看,有虛函數、模板、重載等,從綁定時間來看,能夠分紅靜態多態和動態多態,也稱爲編譯期多態和運行期多態。編程
本文即講述這其中的異同。注意泛型編程和元編程一般都是以模板形式實現的,所以在本文中主要介紹基於面向對象的動態多態和基於模板編程的靜態多態兩種形式。另外其實宏也能夠認爲是實現靜態多態的一種方式,實現原理就是全文替換,但C++語言自己就不喜歡宏,這裏也忽略了「宏多態」。編程語言
動態多態的設計思想:對於相關的對象類型,肯定它們之間的一個共同功能集,而後在基類中,把這些共同的功能聲明爲多個公共的虛函數接口。各個子類重寫這些虛函數,以完成具體的功能。客戶端的代碼(操做函數)經過指向基類的引用或指針來操做這些對象,對虛函數的調用會自動綁定到實際提供的子類對象上去。ide
從上面的定義也能夠看出,因爲有了虛函數,所以動態多態是在運行時完成的,也能夠叫作運行期多態,這造就了動態多態機制在處理異質對象集合時的強大威力(固然,也有了一點點性能損失)。函數
看代碼:工具
1 namespace DynamicPoly 2 { 3 class Geometry 4 { 5 public: 6 virtual void Draw()const = 0; 7 }; 8 9 class Line : public Geometry 10 { 11 public: 12 virtual void Draw()const{ std::cout << "Line Draw()\n"; } 13 }; 14 15 class Circle : public Geometry 16 { 17 public: 18 virtual void Draw()const{ std::cout << "Circle Draw()\n"; } 19 }; 20 21 class Rectangle : public Geometry 22 { 23 public: 24 virtual void Draw()const{ std::cout << "Rectangle Draw()\n"; } 25 }; 26 27 void DrawGeometry(const Geometry *geo) 28 { 29 geo->Draw(); 30 } 31 32 //動態多態最吸引人之處在於處理異質對象集合的能力 33 void DrawGeometry(std::vector<DynamicPoly::Geometry*> vecGeo) 34 { 35 const size_t size = vecGeo.size(); 36 for(size_t i = 0; i < size; ++i) 37 vecGeo[i]->Draw(); 38 } 39 } 40 41 void test_dynamic_polymorphism() 42 { 43 DynamicPoly::Line line; 44 DynamicPoly::Circle circle; 45 DynamicPoly::Rectangle rect; 46 DynamicPoly::DrawGeometry(&circle); 47 48 std::vector<DynamicPoly::Geometry*> vec; 49 vec.push_back(&line); 50 vec.push_back(&circle); 51 vec.push_back(&rect); 52 DynamicPoly::DrawGeometry(vec); 53 }
動態多態本質上就是面向對象設計中的繼承、多態的概念。動態多態中的接口是顯式接口(虛函數),好比,性能
1 void DoSomething(Widget& w) 2 { 3 if( w.size() > 0 && w != someNastyWidget) 4 { 5 Widget temp(w); 6 temp.normalize(); 7 temp.swap(w); 8 } 9 }
對於上面的代碼,這要求:測試
靜態多態的設計思想:對於相關的對象類型,直接實現它們各自的定義,不須要共有基類,甚至能夠沒有任何關係。只須要各個具體類的實現中要求相同的接口聲明,這裏的接口稱之爲隱式接口。客戶端把操做這些對象的函數定義爲模板,當須要操做什麼類型的對象時,直接對模板指定該類型實參便可(或經過實參演繹得到)。優化
相對於面向對象編程中,以顯式接口和運行期多態(虛函數)實現動態多態,在模板編程及泛型編程中,是以隱式接口和編譯器多態來實現靜態多態。spa
看代碼:設計
1 namespace StaticPoly 2 { 3 class Line 4 { 5 public: 6 void Draw()const{ std::cout << "Line Draw()\n"; } 7 }; 8 9 class Circle 10 { 11 public: 12 void Draw(const char* name=NULL)const{ std::cout << "Circle Draw()\n"; } 13 }; 14 15 class Rectangle 16 { 17 public: 18 void Draw(int i = 0)const{ std::cout << "Rectangle Draw()\n"; } 19 }; 20 21 template<typename Geometry> 22 void DrawGeometry(const Geometry& geo) 23 { 24 geo.Draw(); 25 } 26 27 template<typename Geometry> 28 void DrawGeometry(std::vector<Geometry> vecGeo) 29 { 30 const size_t size = vecGeo.size(); 31 for(size_t i = 0; i < size; ++i) 32 vecGeo[i].Draw(); 33 } 34 } 35 36 void test_static_polymorphism() 37 { 38 StaticPoly::Line line; 39 StaticPoly::Circle circle; 40 StaticPoly::Rectangle rect; 41 StaticPoly::DrawGeometry(circle); 42 43 std::vector<StaticPoly::Line> vecLines; 44 StaticPoly::Line line2; 45 StaticPoly::Line line3; 46 vecLines.push_back(line); 47 vecLines.push_back(line2); 48 vecLines.push_back(line3); 49 //vecLines.push_back(&circle); //編譯錯誤,已再也不可以處理異質對象 50 //vecLines.push_back(&rect); //編譯錯誤,已再也不可以處理異質對象 51 StaticPoly::DrawGeometry(vecLines); 52 53 std::vector<StaticPoly::Circle> vecCircles; 54 vecCircles.push_back(circle); 55 StaticPoly::DrawGeometry(circle); 56 }
靜態多態本質上就是模板的具現化。靜態多態中的接口調用也叫作隱式接口,相對於顯示接口由函數的簽名式(也就是函數名稱、參數類型、返回類型)構成,隱式接口一般由有效表達式組成, 好比,
1 template<typename Widget,typename Other> 2 void DoSomething(Widget& w, const Other& someNasty) 3 { 4 if( w.size() > 0 && w != someNasty) //someNastyT多是是T類型的某一實例,也可能不是 5 { 6 Widget temp(w); 7 temp.normalize(); 8 temp.swap(w); 9 } 10 }
這看似要求:
可是,
優勢:
缺點:
優勢:
缺點:
附上本次測試的全部代碼:
1 namespace DynamicPoly 2 { 3 class Geometry 4 { 5 public: 6 virtual void Draw()const = 0; 7 }; 8 9 class Line : public Geometry 10 { 11 public: 12 virtual void Draw()const{ std::cout << "Line Draw()\n"; } 13 }; 14 15 class Circle : public Geometry 16 { 17 public: 18 virtual void Draw()const{ std::cout << "Circle Draw()\n"; } 19 }; 20 21 class Rectangle : public Geometry 22 { 23 public: 24 virtual void Draw()const{ std::cout << "Rectangle Draw()\n"; } 25 }; 26 27 void DrawGeometry(const Geometry *geo) 28 { 29 geo->Draw(); 30 } 31 32 //動態多態最吸引人之處在於處理異質對象集合的能力 33 void DrawGeometry(std::vector<DynamicPoly::Geometry*> vecGeo) 34 { 35 const size_t size = vecGeo.size(); 36 for(size_t i = 0; i < size; ++i) 37 vecGeo[i]->Draw(); 38 } 39 } 40 41 namespace StaticPoly 42 { 43 class Line 44 { 45 public: 46 void Draw()const{ std::cout << "Line Draw()\n"; } 47 }; 48 49 class Circle 50 { 51 public: 52 void Draw(const char* name=NULL)const{ std::cout << "Circle Draw()\n"; } 53 }; 54 55 class Rectangle 56 { 57 public: 58 void Draw(int i = 0)const{ std::cout << "Rectangle Draw()\n"; } 59 }; 60 61 template<typename Geometry> 62 void DrawGeometry(const Geometry& geo) 63 { 64 geo.Draw(); 65 } 66 67 template<typename Geometry> 68 void DrawGeometry(std::vector<Geometry> vecGeo) 69 { 70 const size_t size = vecGeo.size(); 71 for(size_t i = 0; i < size; ++i) 72 vecGeo[i].Draw(); 73 } 74 } 75 76 void test_dynamic_polymorphism() 77 { 78 DynamicPoly::Line line; 79 DynamicPoly::Circle circle; 80 DynamicPoly::Rectangle rect; 81 DynamicPoly::DrawGeometry(&circle); 82 83 std::vector<DynamicPoly::Geometry*> vec; 84 vec.push_back(&line); 85 vec.push_back(&circle); 86 vec.push_back(&rect); 87 DynamicPoly::DrawGeometry(vec); 88 } 89 90 void test_static_polymorphism() 91 { 92 StaticPoly::Line line; 93 StaticPoly::Circle circle; 94 StaticPoly::Rectangle rect; 95 StaticPoly::DrawGeometry(circle); 96 97 std::vector<StaticPoly::Line> vecLines; 98 StaticPoly::Line line2; 99 StaticPoly::Line line3; 100 vecLines.push_back(line); 101 vecLines.push_back(line2); 102 vecLines.push_back(line3); 103 //vecLines.push_back(&circle); //編譯錯誤,已再也不可以處理異質對象 104 //vecLines.push_back(&rect); //編譯錯誤,已再也不可以處理異質對象 105 StaticPoly::DrawGeometry(vecLines); 106 107 std::vector<StaticPoly::Circle> vecCircles; 108 vecCircles.push_back(circle); 109 StaticPoly::DrawGeometry(circle); 110 } 111 112 /**沒法編譯經過,所以Widget要求有顯式接口,但如今看不到*/ 113 //void DoSomething(Widget& w) 114 //{ 115 // if( w.size() > 0 && w != someNastyWidget) 116 // { 117 // Widget temp(w); 118 // temp.normalize(); 119 // temp.swap(w); 120 // } 121 //} 122 123 /**能夠編譯經過,所以此處只是要求了只有在模板具現時需保證下面可編譯(無調用,無具現)*/ 124 template<typename Widget,typename Other> 125 void DoSomething(Widget& w, const Other& someNasty) 126 { 127 if( w.size() > 0 && w != someNasty) //someNastyT多是是T類型的某一實例,也可能不是 128 { 129 Widget temp(w); 130 temp.normalize(); 131 temp.swap(w); 132 } 133 }