多态是一种能够令单一的泛型标记关联不同特定行为的能力。对于面向对象的程序设计而言,多态是一块基石。在C++中,这块基石主要是通过继承和虚函数实现的。由于这两个机制都是(至少一部分)在运行期进行处理的,因此我们把这种多态称为动多态(一般讲的多态就是这种多态)。然而,模板也允许我们使用单一的泛型标记来关联不同的特定行为,但是这种关联是在编译期进行处理的。因此我们把这种叫做静多态
一个典型例子:一个用于管理几何形状,并且能够以某种方式进行修改的应用程序。设计实现:一个声明一些适用于其他所有几何对象的公共操作和属性的抽象基类Geobj,一些派生自Geobj的具体几何对象的类
//poly/coord.hpp
#include
class Coord {
private:
int x, y;
public:
Coord (int i1, int i2) : x(i1), y(i2) {
}
friend Coord operator- (Coord const& c1, Coord const& c2) {
return Coord(c1.x-c2.x, c1.y-c2.y);
}
Coord abs() {
return Coord(std::abs(x),std::abs(y));
}
};
//poly/dynahier.hpp
#include "coord.hpp"
//针对几何对象的公共抽象基类GeoObj
class GeoObj {
public:
// 画出几何对象
virtual void draw() const = 0;
// 返回几何对象的重心
virtual Coord center_of_gravity() const = 0;
//...
};
// 具体的几何对象类GeoObj
// - 派生自 GeoObj
class Circle : public GeoObj {
public:
virtual void draw() const;
virtual Coord center_of_gravity() const;
//...
};
// 具体的几何对象类GeoObj
// - 派生自 GeoObj
class Line : public GeoObj {
public:
virtual void draw() const;
virtual Coord center_of_gravity() const;
//...
};
//...
生成了具体对象之后,客户端代码就可以通过指向基类的引用或者指针来操作这些对象,并且能够通过这些引用或者指针来实现虚函数的调度机制。也就是说,利用一个指向基类(子对象)的指针或者引用来调用虚成员函数,实际上可以调用(指针或者引用实际上所代表的)具体类对象的成员
#include "dynahier.hpp"
#include
// 画任意一个GeoObj
void myDraw (GeoObj const& obj)
{
obj.draw(); // 根据对象的类型调用相应的draw()
}
//计算两个GeoObjs对象之间中心的距离
Coord distance (GeoObj const& x1, GeoObj const& x2)
{
Coord c = x1.center_of_gravity() - x2.center_of_gravity();
return c.abs(); // 返回坐标的绝对值
}
// draw属于异类对象的GeoObjs对象
void drawElems (std::vector<GeoObj*> const& elems)
{
for (unsigned i=0; i<elems.size(); ++i) {
elems[i]->draw(); // 根据对象的类型调用draw()
}
}
int main()
{
Line l;
Circle c, c1, c2;
myDraw(l); // myDraw(GeoObj&) => Line::draw()
myDraw(c); // myDraw(GeoObj&) => Circle::draw()
distance(c1,c2); // distance(GeoObj&,GeoObj&)
distance(l,c); // distance(GeoObj&,GeoObj&)
std::vector<GeoObj*> coll; //元素类型互异的集合
coll.push_back(&l); // insert line
coll.push_back(&c); // insert circle
drawElems(coll); // 画不同种类的 GeoObjs
}
对于动多态而言,最引人注目的特性或许是处理异类容器的能力
模板也能用来实现多态。然而,这种多态并不依赖于在基类中包含公共行为的因素;但仍然存在一种隐式的公共性,即应用程序的不同“形状(即类型)”都必须支持某些使用公共语法的操作(也就是说,相关的函数必须具有相同的名称)。另外,具体类之间的定义是独立的(见下图)。于是,当使用具体类对模板实例化时,就实现了多态
现在我们改写上面的例子。
// poly/coord.hpp
#include
class Coord {
private:
int x, y;
public:
Coord (int i1, int i2) : x(i1), y(i2) {
}
friend Coord operator- (Coord const& c1, Coord const& c2) {
return Coord(c1.x-c2.x, c1.y-c2.y);
}
Coord abs() {
return Coord(std::abs(x),std::abs(y));
}
};
// poly/statichier.hpp
//具体的集合对象类 Circle
// - 没有派生自任何其他的class
class Circle {
public:
void draw() const;
Coord center_of_gravity() const;
//...
};
// 具体的集合对象类 Circle
// - 没有派生自任何其他的class
class Line {
public:
void draw() const;
Coord center_of_gravity() const;
//...
};
//...
// poly/staticpoly.cpp
#include "statichier.hpp"
#include
// 画出任意 GeoObj
template <typename GeoObj>
void myDraw (GeoObj const& obj)
{
obj.draw(); // 根据对象的类型调用相应的draw()
}
//计算两个GeoObjs对象之间中心的距离
template <typename GeoObj1, typename GeoObj2>
Coord distance (GeoObj1 const& x1, GeoObj2 const& x2)
{
Coord c = x1.center_of_gravity() - x2.center_of_gravity();
return c.abs(); // 返回坐标的绝对值
}
// draw homogeneous collection of GeoObjs
template <typename GeoObj>
void drawElems (std::vector<GeoObj> const& elems)
{
for (unsigned i=0; i<elems.size(); ++i) {
elems[i].draw(); // call draw() according to type of element
}
}
int main()
{
Line l;
Circle c, c1, c2;
myDraw(l); // myDraw(GeoObj&) => Line::draw()
myDraw(c); // myDraw(GeoObj&) => Circle::draw()
distance(c1,c2); // distance(GeoObj1&,GeoObj2&)
distance(l,c); // distance(GeoObj1&,GeoObj2&)
// std::vector coll; // ERROR: no heterogeneous
// collection possible
std::vector<Line> coll; // OK: homogeneous collection possible
coll.push_back(l); // insert line
drawElems(coll); // draw all lines
}
静多态没有处理异类容器的能力,这是静多态的静态特性决定的:所有的类型都必须能够在编译器确定。但我们可以为不同的几何对象类型引入不同的集合
严格的说,在C++中,动动态是绑定并且动态的多态的简称,静多态是非绑定的静态的多态的简称
优缺点:
动多态优点:
静多态优点:
通常而言,静多态具有更好的类型安全性:因为静多态在编译期会对所有的绑定操作进行检查。