C++多型性
多型性
在深入瞭解本章之前,您應該對指標和類繼承有適當的瞭解。如果您不確定以下任何表示式的含義,則應檢視指示的內容:
指向基類的指標
類繼承的關鍵特徵之一是指向派生類的指標與指向其基類的指標在型別上相容。多型是利用這一簡單但強大而通用的功能的藝術。
考慮到此功能,可以使用指標重寫有關矩形和三角形類的示例:
// 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;
}
20
10
函式main聲明瞭兩個指向Polygon(名為ppoly1和ppoly2)的指標。這些物件分別被分配了rect和的地址trgl,它們是型別Rectangle和的物件Triangle。這樣的分配是有效的,因為兩者Rectangle並Triangle從派生類Polygon。
取消引用ppoly1和ppoly2(使用ppoly1->和ppoly2->)是有效的,並允許我們訪問其指向的物件的成員。例如,以下兩個語句在上一個示例中是等效的:
ppoly1->set_values (4,5);
rect.set_values (4,5);
但是由於ppoly1and的型別ppoly2都是指向的指標Polygon(而不是Rectangle或指向的指標Triangle),因此只能Polygon訪問從繼承的成員,而不能訪問派生類Rectangle和的成員Triangle。這就是為什麼上面的程式area使用rect和trgl而不是使用指標直接訪問兩個物件的成員的原因。指向基類的指標無法訪問area成員。
成員area可能已經用指標來訪問的Polygon,如果area是的一員Polygon,而不是它的派生類中的一員,但問題是,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;
}
20
10
0
在這個例子中,所有三個類(Polygon,Rectangle和Triangle),由於具有相同的部件:width,height,和功能set_values和area。
成員函式area已virtual在基類中宣告,因為稍後會在每個派生類中對其進行重新定義。非虛擬部件也可以在派生類重新定義,但派生類的非虛擬部件不能通過基類的引用來訪問:即,如果virtual是從宣告除去area在上面的例子中,所有三個呼叫area將返回零,因為在所有情況下,都將呼叫基類的版本。
因此,從本質上講,virtual關鍵字的作用是允許從指標適當地呼叫與基類中同名的派生類的成員,並且更精確地是當指標的型別是指向要指向基類的基類的指標時如上例所示,派生類的物件。
宣告或繼承虛擬函式的類稱為多型類。
請注意,儘管其成員之一具有虛擬性,但Polygon還是一個常規類,其中甚至例項化了一個物件(poly),其自身的成員定義area始終返回0。
抽象基類
抽象基類與上Polygon一個示例中的類非常相似。它們是隻能用作基類的類,因此允許具有無定義的虛擬成員函式(稱為純虛擬函式)。語法是將它們的定義替換為=0(等號和零):
抽象基Polygon類如下所示:
// abstract class CPolygon
class Polygon {
protected:
int width, height;
public:
void set_values (int a, int b)
{ width=a; height=b; }
virtual int area () =0;
};
注意area沒有定義。它已被替換=0,這使其成為一個純虛擬函式。包含至少一個純虛擬函式的類稱為抽象基類。
抽象基類不能用於例項化物件。因此,的最後一個抽象基類版本Polygon不能用於宣告如下物件:
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;
}
20
10
在此示例中,使用唯一型別的指標(Polygon*)引用了不同但相關型別的物件,並且每次呼叫正確的成員函式都是因為它們是虛擬的。在某些情況下這可能非常有用。例如,即使抽象基類的成員本身沒有此功能的實現,它甚至有可能Polygon使用特殊指標this來訪問適當的虛擬成員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;
}
20
10
虛擬成員和抽象類授予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;
}
20
10
請注意,ppoly指標:
Polygon * ppoly1 = new Rectangle (4,5);
Polygon * ppoly2 = new Triangle (4,5);
宣告型別為“指向的指標Polygon”,但已宣告分配的物件具有直接的派生類型別(Rectangle和Triangle)。