C/C++编程:模板的多态

多态是一种能够令单一的泛型标记关联不同特定行为的能力。对于面向对象的程序设计而言,多态是一块基石。在C++中,这块基石主要是通过继承和虚函数实现的。由于这两个机制都是(至少一部分)在运行期进行处理的,因此我们把这种多态称为动多态(一般讲的多态就是这种多态)。然而,模板也允许我们使用单一的泛型标记来关联不同的特定行为,但是这种关联是在编译期进行处理的。因此我们把这种叫做静多态

动多态

  • 动多态是通过继承和虚函数实现的
  • 动多态的设计思想:对于几个相关对象的类型,确定它们之间的一个共同功能集;然后在基类中,把这些共同的功能声明为多个虚函数接口

一个典型例子:一个用于管理几何形状,并且能够以某种方式进行修改的应用程序。设计实现:一个声明一些适用于其他所有几何对象的公共操作和属性的抽象基类Geobj,一些派生自Geobj的具体几何对象的类

C/C++编程:模板的多态_第1张图片

//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
}

对于动多态而言,最引人注目的特性或许是处理异类容器的能力

静多态

模板也能用来实现多态。然而,这种多态并不依赖于在基类中包含公共行为的因素;但仍然存在一种隐式的公共性,即应用程序的不同“形状(即类型)”都必须支持某些使用公共语法的操作(也就是说,相关的函数必须具有相同的名称)。另外,具体类之间的定义是独立的(见下图)。于是,当使用具体类对模板实例化时,就实现了多态
C/C++编程:模板的多态_第2张图片
现在我们改写上面的例子。

//  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++中,动动态是绑定并且动态的多态的简称,静多态是非绑定的静态的多态的简称

  • 通过继承实现的多态是绑定的和动态的
    • 绑定的含义:对于参与多态行为的类型,它们(具有多态行为)的接口是在公共基类的设计中就预先确定的(绑定也叫做入侵或者插入的)
    • 动态的含义:接口的绑定是在运行期(动态)完成的
  • 通过模板实现的多态是非绑定的和静态的
    • 非绑定的含义:对于参与多态行为的类型,它们(具有多态行为)的接口是没有预先定义的(非绑定也叫做非入侵或者非插入的)
    • 静态的含义:接口的绑定是在编译期(静态)完成的

优缺点:

  • 动多态优点:

    • 能够优雅的处理异类集合
    • 可执行代码的大小通过比较小(因为只需要一个多态函数,但对于静多态而言,为了处理不同的代码,必须生成多个不同的实例)
    • 可以对代码进行完全编译,因此并不需要发布实现源码(但是,分发模板库通常都需要同时分发模板实现的源代码)
  • 静多态优点:

    • 可以很容易的实现内建类型的集合。更广义的说,并不需要通过公共基类来表达接口的共同性
    • 所生成的代码效率通常比较高(因为并不存在通过指针的简介调用,而且可以进行演绎的非虚拟函数具有更多的内联机会)
    • 对于只提供部分接口的具体类型,如果在应用程序中只使用这一部分接口,那么也可以使用该具体类型,而不必在乎该类型是否提供其他部分的接口

通常而言,静多态具有更好的类型安全性:因为静多态在编译期会对所有的绑定操作进行检查

你可能感兴趣的:(C++)