Effective C++条款41:模板与泛型编程之(了解隐式接口和编译期多态)

一、面向对象编程中提供的是“显式接口”和“运行期多态”

  • 概述:面向对象编程世界总是以“显式接口”和“运行期多态”解决问题

演示说明

  • 下面定义一个类,用作演示:
class Widget {
public:
    Widget();
    virtual ~Widget();
	
    virtual std::size_t size()const;
    virtual void normalize();
    void swap(Widget& other); //参阅条款25
};
  • 我们再定义一个下面的函数:
void doProcessing(Widget& w)
{
    if (w.size() > 10 && w != someNastyWidget) {
        Widget temp(w);
        w.normalize();
        temp.swap(w);
    }
}
  • 对于doProcessing()函数参数中的w来说:
    • 显式接口:因为w为类类型Widget,因此在使用w对象的时候必须其拥有哪些接口,也就是在源码中必须明确可见
    • 运行期多态:Widget的某些数据成员是virtual的,w对这些函数的调用表现出运行期多态

二、模板编程中提供的是“隐式接口”和“编译器多态”

  • 概述:模板编程世界总是以“隐式接口”和“编译器多态”解决问题

演示说明

  • 我们紧接着“一”中的演示说明,将doProcessing()函数改为模板函数。代码如下:
template<typename T>
void doProcessing(T& w)
{
    if (w.size() > 10 && w != someNastyWidget) {
        Widget temp(w);
        w.normalize();
        temp.swap(w);
    }
}
  • 对于doProcessing()函数参数中的w来说:
    • 隐式接口:由于T的类型未知,但是通过函数的源码我们可以看出来,类型T要支持size()、normalize()、swap()等成员函数、copy构造函数(用以建立temp)、不等比较等。这些对于类型T来说,都是一些隐式接口
    • 编译期多态:只要涉及对w的任何函数调用,例如operator>和operator!=等,就可能造成template具体化,这样的具体行为发生在编译期。“以不同的template参数具体化函数模板”会导致调用不同的函数,这便是所谓的编译期多态

三、显式接口与隐式接口再探

显式接口

  • 显式接口:由函数的签名式(函数名称、参数类型、返回类型)构成
  • 例如Widget class,其public接口由一个构造函数、一个析构函数、成员函数size()、normalize()、swap()构成,以及编译器默认提供的拷贝构造函数和拷贝赋值运算符组成
class Widget {
public:
    Widget();
    virtual ~Widget();
	
    virtual std::size_t size()const;
    virtual void normalize();
    void swap(Widget& other); //参阅条款25
};

隐式接口

  • 隐式接口:其并不基于函数签名式,而是由有效表达式组成
  • 再次查看一下上面的模板函数,T类型的接口必须有下面的约束:
    • 其必须提供一个名为size的成员函数,该函数返回一个整数值
    • 其必须支持一个operator!=函数,用来比较两个T对象
template<typename T>
void doProcessing(T& w)
{
    if (w.size() > 10 && w != someNastyWidget) {
        Widget temp(w);
        w.normalize();
        temp.swap(w);
    }
}
  • 由于运算符重载的也行,上面的两个约束都不需要满足:
    • 对于size()来说,size()可能从基类继承而来,这个成员函数不需要返回一个整数值,甚至不需要返回一个数值类型,其甚至不需要返回一个定义有operator>的类型。它唯一要做的是返回一个类型为X的对象,而X对象加上一个int(10的类型)必须能够调用一个operator>
    • T并不需要支持operator!=,因为以下这种情况也是可以的:operator!=接受一个类型为X的对象和一个类型为Y的对象,T可被转换为X而someNastyWidget的类型可被转换为Y,这样就可以有效调用operator!=
  • 注意:上述并未考虑这样的可能性:operator&&被重载,从一个连接词改变或许完全不同的某种东西,从而改变上述表达式的意义

四、总结

  • classes和templates都支持接口和多态
  • 对classes而言接口是显式的,以函数签名为中心。多态则是通过virtual函数发生于运行期
  • 对template参数而言,接口是隐式的,奠基于有效表达式。多态则是通过template具体化和函数重载解析发生于编译期
发布了1525 篇原创文章 · 获赞 1084 · 访问量 45万+

猜你喜欢

转载自blog.csdn.net/qq_41453285/article/details/104842632