读书笔记 effective c++ Item 41 理解隐式接口和编译期多态
1. 显示接口和运行时多态
面向对象编程的世界围绕着显式接口和运行时多态。举个例子,考虑下面的类(无意义的类),
class Widget {
public:
Widget();
virtual ~Widget();
virtual std::size_t size() const;
virtual void normalize();
void swap(Widget& other); // see Item 25
...
};
考虑下面的函数(同样没有意义),
void doProcessing(Widget& w)
{
if (w.size() > && w != someNastyWidget) {
Widget temp(w);
temp.normalize();
temp.swap(w);
}
}
对于doProcessing中的w,我们可以这样说:
- 因为w被声明为Widget类型,w必须支持Widget接口。我们可以在源码中搜寻这个接口(例如,在Widget的头文件中),以便能够确切的知道它长成什么样子,所以我将其叫做一个显式的接口(explicit interface)——可以显式的在源码中看到的接口。
- 因为Widget中的一些成员函数是虚的,w对这些函数的调用会展示出运行时多态:w具体调用哪个函数会根据运行时w的动态类型来决定。
2. 隐式接口和编译期多态
模板(template)和泛型编程(generic programming)的世界从根本上发生了变化。在这个世界中,显式接口和运行时多态继续存在,但是它们不再像以前那么重要。相反,隐式接口和编译时多态被挪到了前台。为了了解这是什么样子的,我们将doProcessing从函数转换为一个函数模板,看看会发生什么:
template<typename T>
void doProcessing(T& w)
{
if (w.size() > && w != someNastyWidget) {
T temp(w);
temp.normalize();
temp.swap(w);
}
}
现在我们能对doProcessing中的w说些什么呢?
- W必须支持的接口由模板中w需要执行的操作所决定。例如,w的类型T必须支持size,normalize和swap成员函数;拷贝构造函数(来创建temp);和不等比较(同someNastyWidget进行比较)。我们很快就能发现这也不是很精确的,但是对于现在来说足够了。重要的是,这些表达式必须是T所支持的隐式接口,它们对于模板来说必须是有效的以便能够通过编译。
- 对于涉及到w的像operator>和operator!=这样的函数调用,可能涉及到模板的实例化来让这些调用成功。这些实例化在编译期发生。因为用不同的模板参数实例化出来的函数模板会导致不同的函数被调用,这叫做“编译时多态”。
3. 显示接口和隐式接口的区别
3.1 显示接口的特点
即使你永远不使用模板,你也应该熟悉运行时多态和编译期多态的区别,因为这同编译期决定调用哪个重载函数以及运行期决定绑定哪个虚函数是类似的。隐式和显式接口的区别对于模板来说是新的概念,然而,一个显式的接口由函数签名组成,也即是函数名字,参数类型,返回值类型等等。Widget类的公共接口,例如:
class Widget {
public:
Widget();
virtual ~Widget();
virtual std::size_t size() const;
virtual void normalize();
void swap(Widget& other);
};
由一个构造函数,一个析构函数,和函数size,normalize和swap以及参数类型,返回值类型和这些函数的常量性组成。(同样包含编译器生成的拷贝构造函数和拷贝赋值运算符——看Item 5)。它同样可以包含typedef和数据成员,如果你够大胆违反Item22的建议的话(将数据成员声明为private)。虽然在这个例子中没有这么做。
3.2 隐式接口的特点
一个隐式的接口会有很大的不同。它不是基于函数签名。而是由有效表达式组成。再看一下doProcessing模板开始部分的条件表达式:
template<typename T>
void doProcessing(T& w)
{
if (w.size() > && w != someNastyWidget) {
...
T(w的类型)的隐式接口看上去会有如下限制:
- 它必须提供一个名字为size的成员函数并且返回一个整型值。
- 它必须支持!=操作符函数,能够对两个T类型的对象进行比较。(这里,我们假设someNastWidget的类型为T。)
多亏了操作符重载,上面的两个限制都不需要满足。T必须支持一个size成员函数,值得提及的是这个函数可能继承自一个基类。但是这个成员函数没有必要返回一个整型值。甚至不需要返回一个数字类型值。如果这么说的话,它甚至不需要返回operator>定义中所需要的值。他需要的是返回一个类型X的对象,于是可以在一个类型X对象和int(因为10是int型的)型对象上调用operator>。但是Operator>没有必要带一个类型X的参数,因为它也可以带一个类型Y的参数,只要Y可以隐式的转成X就可以了。
类似的,T也没有必要支持operator!=,因为operator!=带一个类型X的参数和一个类型Y的参数也能接受。只要T能转成X并且someNastyWidget的类型可以转换成Y,那么函数调用就是有效的。
(说句题外话,这个分析没有考虑将operator&&进行重载的可能性,这样就将上面的表达式的意思从一个连接词转换成了其它的意义迥然的东西。)
大多数人当第一次开始考虑这种隐式转换就头疼,你不需要吃阿司匹林。隐式接口只是简单的由一些有效表达式组成。表达式本身看起来复杂,但是加在上面的限制一般来说是简单直接的。例如,考虑下面的条件表达式,
if (w.size() > && w != someNastyWidget) ...
很难说要对函数size,operator>,operator&&或者operator!=做什么限制,但是很容易辨认出需要对整个表达式做出的限制。If声明的条件部分必须是一个boolean表达式,所以不管涉及到什么类型,也不管w.size() > 10 && w != someNastyWidget产生什么,它必须同bool是兼容的。这是模板doProcessing强加在类型参数T上的隐式接口的一部分。剩下的doProcessing所需要的接口就是对拷贝构造函数的调用,还有swap对于类型T来说必须是有效的。
强加在模板参数上的隐式接口同强加在类对象上的显示接口一样真实,两者都是在编译阶段检查。你不能同一个类提供的显示接口相矛盾的方式使用一个类对象(不会编译通过),你也不能随便在一个模板中尝试使用一个对象,除非这个对象支持模板需要的隐式转换(否则也不能通过编译)
4. 总结
- 类和模板都支持接口和多态。
- 对于类来说,接口是显示的,以函数签名为中心。多态发生在运行时,通过虚函数来实现。
- 对于模板参数来说,接口是隐式的,基于有效表达式。模板多态通过模板实例化和函数重载来实现,它发生在编译期。
读书笔记 effective c++ Item 41 理解隐式接口和编译期多态的更多相关文章
- Effective C++ Item 41 了解隐式接口和编译期多态
本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 经验:class 和 templates 都支持接口和多态. 对 classes 而言接口是 ...
- 读书笔记_Effective_C++_条款四十一:了解隐式接口和编译期多态
从本条款开始,就进入了全书的第七部分:模板与泛型编程.模板与泛型在C++中是非常重要的部分,还记得本书第一章时,把C++视为一个联邦,它由四个州政府组成,其中一个政府就是模板与泛型了. 本条款是一个介 ...
- Effective C++ -----条款41:了解隐式接口和编译期多态
classes和templates都支持接口(interface)和多态(polymorphism). 对classes而言接口是显式的(explicit),以函数签名为中心.多态则是通过virtua ...
- [EffectiveC++]item41:了解隐式接口和编译期多态
- 读书笔记 effective c++ Item 30 理解内联的里里外外 (大师入场啦)
最近北京房价蹭蹭猛涨,买了房子的人心花怒放,没买的人心惊肉跳,咬牙切齿,楼主作为北漂无房一族,着实又亚历山大了一把,这些天晚上睡觉总是很难入睡,即使入睡,也是浮梦连篇,即使亚历山大,对C++的热情和追 ...
- 读书笔记 effective c++ Item 49 理解new-handler的行为
1. new-handler介绍 当操作符new不能满足内存分配请求的时候,它就会抛出异常.很久之前,它会返回一个null指针,一些旧的编译器仍然会这么做.你仍然会看到这种旧行为,但是我会把关于它的讨 ...
- 读书笔记 effective c++ Item 42 理解typename的两种意义
1. class和typename意义相同的例子 问题:在下面的模板声明中class和typename的区别是什么? template<class T> class Widget; // ...
- 读书笔记 effective c++ Item 42 理解typename的两种涵义
1. class和typename含义相同的例子 问题:在下面的模板声明中class和typename的区别是什么? template<class T> class Widget; // ...
- 读书笔记 effective c++ Item 19 像设计类型(type)一样设计
1. 你需要重视类的设计 c++同其他面向对象编程语言一样,定义了一个新的类就相当于定义了一个新的类型(type),因此作为一个c++开发人员,大量时间会被花费在扩张你的类型系统上面.这意味着你不仅仅 ...
随机推荐
- Java模拟新浪微博登陆抓取数据
前言: 兄弟们来了来了,最近有人在问如何模拟新浪微博登陆抓取数据,我听后默默地抽了一口老烟,暗暗的对自己说,老汉是时候该你出场了,所以今天有时间就整理整理,浅谈一二. 首先: 要想登陆新浪微博需要 ...
- 读书笔记 effective c++ Item 13 用对象来管理资源
1.不要手动释放从函数返回的堆资源 假设你正在处理一个模拟Investment的程序库,不同的Investmetn类型从Investment基类继承而来, class Investment { ... ...
- 如何用docker安装laravel开发环境
如何用docker安装laravel开发环境 看laravel框架的官方文档安装部分时,发现需要安装特别多软件,估计还有许多复杂的配置,官方推荐使用Laravel Homestead虚拟机进行安装,但 ...
- Memcached服务安装
安装Memcached服务 memcache分为服务端和客户端程序 服务端程序用来支持存储k-v值,程序名称memcached 客户端与服务端通信,进行存取值(常用的如php的memcache扩展,m ...
- Oracle排序分析函数
在Oracle自拓展SQL功能中,分析函数(Analytical Function)是非常强大的工具. 本篇我们介绍几个Oracle典型的排序分析函数,来帮助我们解决实际问题. 1.从rownum谈起 ...
- ERP库位分布看板(库位管理)
客户正在使用的看板管理,根据厂家需求,做的二次开发. 一:看板效果 二:客户需求 1.客户需求:XX是一家汽车零部件(胶管,硅胶管等)的生产厂家,因此对原料,半成品的有效期有严格的要求. 多次调研得知 ...
- tp框架的详细介绍,tp框架基础
php框架 真实项目开发步骤: 多人同时开发项目,协作开发项目.分工合理.效率有提高(代码风格不一样.分工不好) 测试阶段 上线运行 对项目进行维护.修改.升级(单个人维护项目,十分困难,代码风格不一 ...
- grpc-gateway:grpc对外提供http服务的解决方案
我所在公司的项目是采用基于Restful的微服务架构,随着微服务之间的沟通越来越频繁,就希望可以做成用rpc来做内部的通讯,对外依然用Restful.于是就想到了google的grpc. 使用grpc ...
- Delete Node in a Linked List leetcode
Write a function to delete a node (except the tail) in a singly linked list, given only access to th ...
- 1230: [Usaco2008 Nov]lites 开关灯
1230: [Usaco2008 Nov]lites 开关灯 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1162 Solved: 589[Sub ...