多态 (Polymorphism)
1.基类的指针(Pointers to base class)
继承的好处之一是一个指向子类(derived class)的指针与一个指向基类(base class)
的指针是 type-compatible 的。本节就是重点介绍如何利用 C++的这一重要特性。例如,
我们将结合 C++的这个功能,重写前面小节中关于长方形 rectangle 和三角形 triangle
的程序:
// pointers to base class
#include
class CPolygon //定义一个多边形的类
{
protected:
int width, height;
public:
void set_values (int a, int b)
{
width=a; height=b;
}
};
class CRectangle: public CPolygon //定义一个继承自CPolygon 的 class CRectangle
{
public:
int area (void)
{
return (width * height);
}
};
class CTriangle: public CPolygon //定义一个继承自CPolygon 的 class CTriangle
{
public:
int area (void)
{
return (width * height / 2);
}
};
int main ()
{
CRectangle rect;
CTriangle trgl;
CPolygon * ppoly1 = ▭ //定义CPolygon的指针分别指向rect 和 trg1
CPolygon * ppoly2 = &trgl;
ppoly1->set_values (4,5);
ppoly2->set_values (4,5);
cout << rect.area() << endl;
cout << trgl.area() << endl;
return 0;
}
结果:
20
10
在主函数 main 中定义了两个指向 class CPolygon 的对象的指针,即 *ppoly1 和
*ppoly2。 它们被赋值为 rect 和 trgl 的地址,因为 rect 和 trgl 是 CPolygon 的子
类的对象,因此这种赋值是有效的。
使用*ppoly1 和 *ppoly2 取代 rect 和 trgl 的唯一限制是*ppoly1 和 *ppoly2 是
CPolygon* 类型的,因此我们只能够引用 CRectangle 和 CTriangle 从基类 CPolygon
中继承的成员。正是由于这个原因,我们不能够使用*ppoly1 和 *ppoly2 来调用成员函
数 area(),而只能使用 rect 和 trgl 来调用这个函数。
要想使 CPolygon 的指针承认 area()为合法成员函数,必须在基类中声明它,而不能只
在子类进行声明。
2.虚拟成员(Virtual members)
如果想在基类中定义一个成员留待子类中进行细化,我们必须在它前面加关键字
virtual ,以便可以使用指针对指向相应的对象进行操作。
请看一下例子:
// virtual members
#include
class CPolygon
{
protected:
int width, height;
public:
void set_values (int a, int b)
{
width=a;
height=b;
}
virtual int area (void)
{
return(0);
}
};
class CRectangle: public CPolygon
{
public:
int area (void)
{ return (width *height); }
};
class CTriangle: public CPolygon
{
public:
int area (void)
{
return (width * height / 2);
}
};
int main ()
{
CRectangle rect;
CTriangle trgl;
CPolygon poly;
CPolygon * ppoly1 = ▭
CPolygon * ppoly2 = &trgl;
CPolygon * ppoly3 = &poly;
ppoly1->set_values (4,5);
ppoly2->set_values (4,5);
ppoly3->set_values (4,5);
cout << ppoly1->area() << endl;
cout << ppoly2->area() << endl;
cout << ppoly3->area() << endl;
return 0;
}
结果:
20
10
0
现在这三个类(CPolygon, CRectangle 和 CTriangle) 都有同样的成员:width, height,
set_values() 和 area()。 area() 被定义为 virtual 是因为它后来在子类中被细化了。你可
以做一个试验,如果
在代码种去掉这个关键字(virtual),然后再执行这个程序,三个多边形的面积计算结果
都将是 0 而不是 20,10,0。这是因为没有了关键字 virtual ,程序执行不再根据实际对
象的不用而调用相应 area() 函数(即分别为 CRectangle::area(), CTriangle::area()
和 CPolygon::area()),取而代之,程序将全部调用 CPolygon::area(),因为这些调用
是通过 CPolygon 类型的指针进行的。
因此,关键字 virtual 的作用就是在当使用基类的指针的时候,使子类中与基类同名的
成员在适当的时候被调用,如前面例子中所示。
注意,虽然本身被定义为虚拟类型,我们还是可以声明一个 CPolygon 类型的对象并调
用它的 area() 函数,它将返回 0 ,如前面例子结果所示。
3.抽象基类(Abstract base classes)
基本的抽象类与我们前面例子中的类 CPolygon 非常相似,唯一的区别是在我们前面的
例子中,我们已经为类 CPolygon 的对象(例如对象 poly)定义了一个有效地 area()函
数,而在一个抽象类(abstract base class)中,我们可以对它不定义,而简单得在函
数声明后面写 =0 (等于 0)。
类 CPolygon 可以写成这样:
// abstract class CPolygon
class CPolygon
{
protected:
int width, height;
public:
void set_values (int a, int b) {
width=a;
height=b;
}
virtual int area (void) =0;
};
注意我们是如何在 virtual int area (void)加=0 来代替函数的具体实现的。这种函
数被称为纯虚拟函数(pure virtual function),而所有包含纯虚拟函数的类被称为抽
象基类(abstract base classes)。
抽象基类的最大不同是它不能够有实例(对象),但我们可以定义指向它的指针。因此,
像这样的声明:
CPolygon poly;
对于前面定义的抽象基类是不合法的。
然而,指针:
CPolygon * ppoly1;
CPolygon * ppoly2
是完全合法的。这是因为该类包含的纯虚拟函数(pure virtual function) 是没有被实
现的,而又不可能生成一个不包含它的所有成员定义的对象。然而,因为这个函数在其
子类中被完整的定义了,所以生成一个指向其子类的对象的指针是完全合法的。
下面是完整的例子:
// virtual members
#include
class CPolygon //定义抽象基类
{
protected:
int width, height;
public:
void set_values (int a, int b)
{
width=a;
height=b;
}
virtual int area (void) =0;
};
class CRectangle: public CPolygon
{
public:
int area (void) { return (width *
height);
}
};
class CTriangle: public CPolygon
{
public:
int area (void)
{
return (width * height / 2);
}
};
int main () {
CRectangle rect;
CTriangle trgl;
CPolygon * ppoly1 = ▭
CPolygon * ppoly2 = &trgl;
ppoly1->set_values (4,5);
ppoly2->set_values (4,5);
cout << ppoly1->area() << endl;
cout << ppoly2->area() << endl;
return 0;
}
结果:
20
10
再看一遍这段程序,你会发现我们可以用同一种类型的指针(CPolygon*)指向不同类的对
象,至一点非常有用。 想象一下,现在我们可以写一个 CPolygon 的成员函数,使得它
可以将函数 area()的结果打印到屏幕上,而不必考虑具体是为哪一个子类。
// virtual members
#include
class CPolygon
{
protected:
int width, height;
public:
void set_values (int a, int b)
{
width=a;
height=b;
}
virtual int area (void) =0;
void printarea (void) //在抽象基类中声明printarea函数 便于后面的调用
{
cout << this->area() << endl;
}
};
class CRectangle: public CPolygon
{
public:
int area (void)
{
return (width *height);
}
};
class CTriangle: public CPolygon
{
public:
int area (void) {
return (width * height / 2);
}
};
int main () {
CRectangle rect;
CTriangle trgl;
CPolygon * ppoly1 = ▭
CPolygon * ppoly2 = &trgl;
ppoly1->set_values (4,5);
ppoly2->set_values (4,5);
ppoly1->printarea();
ppoly2->printarea();
return 0;
}
结果:
20
10
记住,this 代表代码正在被执行的这一个对象的指针。
抽象类和虚拟成员赋予了 C++ 多态(polymorphic)的特征,使得面向对象的编程
object-oriented programming 成为一个有用的工具。这里只是展示了这些功能最简单
的用途。想象一下如果在对象数组或动态分配的对象上使用这些功能,将会节省多少麻
烦。
用户1842909 2015-8-2 16:33
用户1836130 2015-7-9 22:30
用户1271441 2015-6-6 10:16
john_xjm_149062208 2015-4-30 09:05
用户373711 2014-2-22 15:27