条款41、隐式接口和编译期多态

	C++ template最初的发展动机:建立“类型安全type safe”的容器如vector,lis,map.后面随着使用的增多,我们发现模版有能力完成更多可能的变化。
	容器虽好,但泛型编程(generic programming)更好:代码和其所处理的对象类型彼此独立。STL算法如for_each,find等就是这类编程的成果。
	于是导出了模板元编程(tmplate metaprogramming):”C+编译器内执行,并于编译完成时停止执行”
面向对象编程总是以显式接口(explicit interfaces)和运行期多态(runtime polymorphism)解决问题.
class Widget
{
         public:
         Widget();
         virtual  ~Widget();
         virtual std::size_t size()const;
         virtual void normalize();
         void swap(Widget&other);    //TK25
}
void doProcess(Widget& w) //无意义的函数
{
         if(w.size()>10&&w!=someNastyWidget)
{
         Widget tmp(w);
         tmp.normalize();
         tmp.swap(w);
}
} //我们可以说doProcess内的w

从上面代码中我们知道:
1、w的类型被声明为Widget,w必须支持Widget接口。我们可以在源码中找到这个接口(Widget的.h文件)。我们称此为一个显示接口、即它在源码中明确可见
2、Widget的成员函数有virtual的,w对那些函数表现出运行时多态。即根据动态类型决定调用哪一个函数。
模版和范型编程与面向对象有根本上的不同,虽然显示接口和运行多态依然存在,但重要性变低。而隐式接口和编译期多态相对更重要。考虑以下代码:
template<typename T>
void doProcess(T& w) {
         if(w.size()>10&&w!=someNastyWidget)
{
         T  tmp(w);
         tmp.normalize();
         tmp.swap(w);
}
}

从上述代码我们可以知道:
1、w支持哪种接口,由模版执行于w上的操作决定。如本例中w必须支持size,normalize,swap,copy构造、不等比较等(虽然不完全正确)。这组表态式(对该模板而言必须有效编译)是T必须支持的一组隐式接口。
2、涉及w的调用,可能实例化模板。“以不同的模板参数实例化函数模版”会导致不同的函数调用。这就是所谓的编译期多态
类似于“哪一个重载函数被调用”(发生在编译期)和“哪一个虚函数被绑定”(运行期)。
 
通常显式接口由函数签名式(函数名,参数类型、返回类型)构成.如
class Widget
{
         public:
         Widget();
         virtual  ~Widget();
         virtualstd::size_t size() const;
         virtualvoid normalize();
         void swap(Widget&other);    //TK25
}

如上,public接口由一个构造、一个析构、三个函数及其参数类型、返回值、常量性构成。当然也包括编译产生的复制构想和copy assignmeng操作符。当然也可以也包括typedef等。
隐式接口完全不同,并不基于签名式,而是由有效表达式组成。再次看下面函数模版的初始条件:
template<typename T>
void doProcess(T& w) {
         if(w.size()>10&&w!=someNastyWidget)
         ……
}

T的隐式接口似乎有这些约束
1、它必须提供一个size函数,该函数返回一个整数值。
2、必须支持一个operator!=  函数,用于比较两个T对象。这里我们假设someNastyWidget类型为T

	这要感谢操作符重载带来的可能性,这两个约束都不需要满足。虽然T必须要支持size函数,但这个函数可以从基类继承而来。同时这个成员函数甚至不要返回一个整数值或整数类型,更甚至不要定义operator>操作类型。唯一要做的是返回一个X对象,这个对象加个一个int(10)的类型就能调用一个operator>。同时operator不需要非取得一个类型为X的参数不可,也可以取得类型为Y的参数,只需要一个隐式转换可以将X转换成Y对象。
         同理类推:T并不需要支持operator=,因为这样也是可以的:operator!= 接受一个类型为X和类型为Y的对象,T可以转换为X而someNastyWidget可以转换为Y。这样就可以直接调用operator!=

上面表达方式或许很复杂,但实际而言却不是,因为它们的约束条件相当直接又明确。如:
if(w.size()>10&&w!=someNastyWidget)

	如其约束条件,虽然多,但整体却很容易。if条件式必须是个bool表达式。无论涉及什么也无论表达式导致什么,只需与bool兼容就行了。这是模版函数doProcess加诸于其他类型参数T的隐式接口的一部分。该函数要求的其他隐式接口:如copy构造、normalize、swap也必须对T型对象有效。

	模版参数身上的隐式接口,和类对象身上的显式接口一样。都在编译期间完成检查。“与类提供显示接口矛盾”的方式使用对象    和   在模版中使用“不支持模版要求的隐式接口”对象都不能通过编译。
 
需要记住的:
	1、类和模版都支持接口和多态。
	2、类接口是显示的,以函数声明为中心。多态通过虚函数发生于运行期。
	3、对模版参数而言,接口是隐式的,基于有效表达式。多态通过模版实例化和函数重载解析,发生于编译期。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值