条款41:了解隐式接口和编译期多态

来源:互联网 发布:东方行知钢琴进修学校 编辑:程序博客网 时间:2024/04/28 21:06

条款41:了解隐式接口和编译期多态
(understand implicit interfaces and compile-time polymorphism.)

内容:
     今天遇到这样一个处理Widget的函数doProcessing,其实现如下:
     void doProcessing(Widget& w)
     {
          if( w.size() > 10 && w != someNastyWidget ){
               Widget temp( w );
               temp.normalize();
               temp.swap( w );
          }
     }
     看完上面的代码以后,我们可以立即得到下面两个简单的结论:
     (1) 由于w是一个Widget类对象,故其必须支持Widget的接口,我们可以在Widget定义的源码中找到这样的接口(一般在Widget的头文件中声明接口),如此的接口我们通常称之为显式接口(explicit interface),也就是源码中明确可见的接口.为了方便一下描述,我们假设其类的简略声明如下:
     class Widget{
     public:
         Widget();
         virtual ~Widget();
         virtual std::size_t size()const;
         virtual void normalize();
         void swap(Widget& other);
         ...
     };
     (2) 由于Widget的某些成员函数是virtual函数,w的引用对象对那些函数的调用将表现出运行期多态(runtime polymorphism),也就是说于运行期根据w的动态类型决定调用哪一个函数.
     当我们在面向对象编程世界中畅游时,对于这种类的显式接口声明与运行期多态性的调用,我们已经很习以为常了.然而Template及泛型编程的时代的来临降低了显式接口与运行期多态的重要性.替换它们的是隐式(implicit intefaces)和编译期多态(comile-time polymorphism),想了解它们是什么吗?
     那我们先将doProcessing从函数转变成函数模板,看此过程中发生了哪些变化:
     template<typename T>
     void doProcessing(T& w)
     {
          if( w.size() > 10 && w != someNastyWidget ){
                T temp( w );
                temp.normalize();
                temp.swap( w );
          }
     }
     那我们重新审视一下这个接口,我们可以得出一些结论:
     (1) w必须支持哪一种接口,系由template中执行w身上的操作来决定.本例看来w的类型T好像必须支持size,normalize和swap成员函数、copy构造函数(用以建立temp)、不等比较(inequality comparision,用来比较someNastyWidget).我们很快就会看到这并非完全正确,当对目前而言足够真实.重要的是,这一组表达式(对此template而言必须有效编译)便是T必须支持的一组隐式接口(implicit interface).
     (2) 凡涉及到w的任何调用,例如operator>和operator!=,有可能造成template具现化(instantiated),使这些调用得以成功.这样的具现行为发生在编译期."以不同的template参数具现化function templates"会导致调用不同的函数,这些便是所谓的编译期多态(compile-time polymorphism).
     通常显式接口由函数签名式(也就是函数名称、参数类型、返回类型)构成,而隐式接口就完全不同了.它并不是基于函数签名式,而是由有效表达式(valid expression)组成.加诸于template参数身上的隐式接口,就像加诸于class对象身上的显式接口一样真实,而且两者都在编译器完成检查.就像你无法以一种"与class提供之显式接口矛盾"的方式来使用对象(代码将通不过编译),你无法在template中使用"不支持template所要求之隐式接口"的对象(代码一样编译不过).
     请记住:
     ■ class和templates都支持接口(interfaces)和多态(polymorphism).
     ■ 对classes而言接口是显式的(explicit),以函数签名为中心.多态则是通过virtual函数发生于运行期.
     ■ 对template参数而言,接口是隐式的(implicit),奠基于有效表达式.多态则是通过template具现化和函数重载解析(function overloading resolution)发生于编译期.