原文来自点击打开链接
www.cplusplus.com使用google搜索C++polymorphic显示的第一个网页便是该网页.
ps(www.cplusplus.com是我平时最为常用的C++文档网站,强烈推荐)
C++ 多态机制
在更深入的了解本章节前,你需要对于指针与类的继承有初步的理解.如果你不是很确定是否理解以下的表达式,你需要复习下列的章节
Statement: | Explained in: |
int A::b(int c) { } | |
a->b | |
class A: public B {}; | 友元与继承 |
指向基类的指针
关于类的继承,有一个特性特别的关键,那就是继承类允许具有可相容其基类对象的指针,多态正是基于这一简单但是有效并且多用特性的艺术.
以下例子,真是基于以上特性,利用指针来完成可复写的三角与矩形类.
/ /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 () {
Rectanglerect;
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中,声明两个指向 Polygon
的指针(命名为ppoly1 和 ppoly2),他们被分别用于指向 继承同一基类的rectangle
类tect与Traiang
类trgl.因此以下两个表达式都是等价的
ppoly1->set_values (4,5); rect.set_values (4,5);
然而因为ppoly1与ppoly2是指向Polyon,而非指向Rectangle与Triangle.因此只有继承自Polygon类的成员允许被访问.而来自Retangle与Traingle不许被访问.这就是为什么我在这里使用类rect与trgl而不是指针(ppoly1与ppoly2)来访问area函数,指向基类的指针是无法访问成员函数area的.
只有当area是基类Polygon的成员,而非其继承类的成员时,使用指向基类的指针允许访问area成员.但是问题在于Retangle与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; }
在上述的例子中,使用了三个类(Polygon,Rectangle,Triangle)具有相同的成员:width,heigh与相同的函数set_value area.
成员函数area在继承类中被声明成虚函数.非虚函数也在继承类中被声明,然而这些非虚函数,无法通过基类的引用被调用,比如,如果area函数关键字virtual被移除,那么在上述的例子中,三个对area的访问将返回0,因为在上述的例子中,基类的函数版本将会在本次的访问中被调用.
因此,本质上,关键字virtual被用于使用以允许指针访问成员函数,该继承类函数与与基类同名.更精确的说在上述例子中,上述类型的指针式指向继承类的基类指针.
一个类能共被声明或者继承一个虚函数是被称作是多态类.
尽管具有一个虚函数,Polygon依旧是一个允许被实例化的普通类(poly),本身的成员函数area依旧可以返回0.
抽象基类
抽象基类与此前的Polygon基类类似,其是只被允许被当做基类来使用.因此其允许拥有不需要定义的函数(纯虚函数).
以下为一个具体的纯虚类Polygon
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 mypolygon; // 由于Polygon是抽象基类因此这里无法工作
但是抽象基类并非是无用的.它能共用于定义指向抽象基类的指针,其能共使用基类的多态属性.
| Polygon * ppoly1; Polygon * ppoly2; |
该类指向继承类的指针是可以被解引用的.
class Polygon {
protected:
int width, height;
public:
void set_values (int a, int b)
{ width=a; height=b; }
virtual int area () =0;
};
在上述的例子中,两个不同却又相关的类型使用相同的指针(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; }
虚函数与抽象基类在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指针:
olygon * ppoly1 = new Rectangle (4,5); Polygon * ppoly2 = new Triangle (4,5);
是被声明成Polygon类型,但是依旧动态构造成指向继承类的指针