在深入這一章之前,你應該對指針和類繼承有一個正確的理解。如果您不確定下列表達式中的任何一個含義,則應查看所指示的部分: 
類繼承的一個重要特征是指向派生類的指針與指向基類的指針類型兼容。多態性是利用這個簡單但功能強大且功能多樣的特性。
關于矩形和三角形類的例子可以使用指針重寫這個功能:
// pointers to base class#include <iostream>using namespace std;class Polygon { PRotected: int width, height; public: void set_values (int a, int b) { width=a; height=b; }};class Rectangle: public Polygon { public: int area() { return width*height; }};class Triangle: public Polygon { public: int area() { return width*height/2; }};int main () { Rectangle rect; Triangle trgl; Polygon * ppoly1 = ▭ Polygon * ppoly2 = &trgl; ppoly1->set_values (4,5); ppoly2->set_values (4,5); cout << rect.area() << '/n'; cout << trgl.area() << '/n'; return 0;}Main函數聲明兩個指針多邊形類(命名為ppoly1和ppoly2)。這些都是直接和rect 與trgl分配地址,分別是類型Rectangle 和Triangle的實體。因為矩形和三角形都是來自多邊形的類,這樣的賦值是有效的。
引用ppoly1和ppoly2(和 ppoly1 ppoly2)也是有效的,這也可以使我們能夠訪問他們的指向對象的成員。例如,以下兩個語句在前面的示例中是等效的:
ppoly1->set_values (4,5);rect.set_values (4,5);但由于ppoly1和ppoly2類型是指針指向多邊形Polygon (而不是指針也指向矩形三角形),只有從多邊形Polygon 繼承的成員可以訪問,而不是其他的派生類,矩形和三角形。這就 是為什么上面的程序訪問的對象用rect 和trgl直接訪問,而不是指針;對基類不能訪問area 成員的指針。
如果area 是多Polygon 多邊形的成員而不是它的派生類的成員,那么area 成員是可以訪問指針成員,但問題是,Rectangle 和Triangle是實現不同area 的版本,因此不存在一個單一的普通版本,可以在基類中實現。
虛擬成員是可以在派生類中重新定義的成員函數,同時通過引用保留其調用屬性。函數成為虛的語法是用虛擬關鍵字virtual 進行其聲明:
// virtual members#include <iostream>using namespace std;class Polygon { protected: int width, height; public: void set_values (int a, int b) { width=a; height=b; } virtual int area () { return 0; }};class Rectangle: public Polygon { public: int area () { return width * height; }};class Triangle: public Polygon { public: int area () { return (width * height / 2); }};int main () { Rectangle rect; Triangle trgl; Polygon poly; Polygon * ppoly1 = ▭ Polygon * ppoly2 = &trgl; Polygon * ppoly3 = &poly; ppoly1->set_values (4,5); ppoly2->set_values (4,5); ppoly3->set_values (4,5); cout << ppoly1->area() << '/n'; cout << ppoly2->area() << '/n'; cout << ppoly3->area() << '/n'; return 0;}在這個例子中,所有的三類(多邊形、矩形和三角形)有相同的成員:width,height,和函數set_values以及area。
成員函數area 在基類中聲明為virtual 虛函數,因為它在每個派生類中被重新定義。非虛成員也可以在派生類中重新定義,但派生類的非虛成員不能通過基類的引用訪問:即,如果virtual是從上面的area 刪除后,后面所有的調用area 就會返回0,因為在所有的情況下,基類的版本將被代替。
因此,從本質上講,是virtual 的關鍵詞是允許派生類具有相同名稱的成員作為一個基類的指針調用,更準確地說,當指針的類型是基類指針,指向派生類對象,如在上面的例子中。
聲明或繼承虛函數的類稱為多態類。
值得注意的是,盡管其成員之一的虛擬性,Polygon 是一個普通類,其中甚至一個對象用自己定義的成員被實例化(poly),area也是總返回0。
抽象基類與前面示例中的多邊形Polygon 類非常相似。它們是只能用作基類的類,因此可以不定義虛擬成員函數(稱為純虛函數)。語法是用 = 0(等號和零)替換它們的定義:
抽象基類Polygon 可以表示為:
// abstract class CPolygonclass Polygon { protected: int width, height; public: void set_values (int a, int b) { width=a; height=b; } virtual int area () =0;};注意,area沒有定義,這已被替換為= 0,這使得它純粹的虛函數。包含至少一個純虛函數的類稱為抽象基類。
抽象類不能實例化對象。因此,這最后一個抽象的基類版本的多邊形不能用來聲明對象:
Polygon mypolygon; // not working if Polygon is abstract base class但抽象基類不是完全無用的。它可以用來創建指向它的指針,并利用它的多態能力。例如,下列指針聲明將是有效的:
Polygon * ppoly1;Polygon * ppoly2;而實際上可以被引用時,對象指向派生類(非抽象)。下面是整個例子:
// abstract base class#include <iostream>using namespace std;class Polygon { protected: int width, height; public: void set_values (int a, int b) { width=a; height=b; } virtual int area (void) =0;};class Rectangle: public Polygon { public: int area (void) { return (width * height); }};class Triangle: public Polygon { public: int area (void) { return (width * height / 2); }};int main () { Rectangle rect; Triangle trgl; Polygon * ppoly1 = ▭ Polygon * ppoly2 = &trgl; ppoly1->set_values (4,5); ppoly2->set_values (4,5); cout << ppoly1->area() << '/n'; cout << ppoly2->area() << '/n'; return 0;}在本例中,使用一個唯一類型的指針(Polygon *)來表示不同類型但相關類型的對象,每次都調用適當的成員函數,只是因為它們是虛的。這在某些情況下真的很有用。例如,即使抽象多邊形本身沒有此函數的實現,抽象基類多邊形的成員也可以使用特殊指針來訪問適當的虛擬成員:
// pure virtual members can be called// from the abstract base class#include <iostream>using namespace std;class Polygon { protected: int width, height; public: void set_values (int a, int b) { width=a; height=b; } virtual int area() =0; void printarea() { cout << this->area() << '/n'; }};class Rectangle: public Polygon { public: int area (void) { return (width * height); }};class Triangle: public Polygon { public: int area (void) { return (width * height / 2); }};int main () { Rectangle rect; Triangle trgl; Polygon * ppoly1 = ▭ Polygon * ppoly2 = &trgl; ppoly1->set_values (4,5); ppoly2->set_values (4,5); ppoly1->printarea(); ppoly2->printarea(); return 0;}在C++虛擬成員和抽象類多態性的特點,對于面向對象的項目最有用。當然,上面的例子是非常簡單的用例,但這些特性可以應用于對象數組或動態分配對象。
這里有一個例子,結合一些功能在最新的章節,如動態內存,構造函數初始化器和多態性:
// dynamic allocation and polymorphism#include <iostream>using namespace std;class Polygon { protected: int width, height; public: Polygon (int a, int b) : width(a), height(b) {} virtual int area (void) =0; void printarea() { cout << this->area() << '/n'; }};class Rectangle: public Polygon { public: Rectangle(int a,int b) : Polygon(a,b) {} int area() { return width*height; }};class Triangle: public Polygon { public: Triangle(int a,int b) : Polygon(a,b) {} int area() { return width*height/2; }};int main () { Polygon * ppoly1 = new Rectangle (4,5); Polygon * ppoly2 = new Triangle (4,5); ppoly1->printarea(); ppoly2->printarea(); delete ppoly1; delete ppoly2; return 0;}請注意ppoly指針:
Polygon * ppoly1 = new Rectangle (4,5);Polygon * ppoly2 = new Triangle (4,5);聲明的類型是“指向多邊形的指針”,但已分配的對象已被直接聲明為派生類類型(矩形和三角形)。
新聞熱點
疑難解答
圖片精選