一、面向对象编程中提供的是“显式接口”和“运行期多态”
- 概述:面向对象编程世界总是以“显式接口”和“运行期多态”解决问题
演示说明
- 下面定义一个类,用作演示:
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具体化和函数重载解析发生于编译期