读书笔记 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++开发人员,大量时间会被花费在扩张你的类型系统上面.这意味着你不仅仅 ...
随机推荐
- React-intl 实现多语言
前言 React 做国际化,我推荐使用 React-intl , 这个库提供了 React 组件和Api两种方式来格式化日期,数字和字符串等.知道这个库了,那让我们开始使用它 组件用法 为了和Reac ...
- Docker网络代理设置
背景 在一些实验室环境,服务器没有直接连接外网的权限,需要通过网络代理.我们通常会将网络代理直接配置在/etc/environment./etc/profile之类的配置文件中,这对于大部分操作都是可 ...
- CSS里padding和margin的区别是什么?
通俗地说——padding 就是内容与边框的距离:margin 就是边框与其他元素的距离.
- Apache Pig处理数据示例
Apache Pig是一个高级过程语言,可以调用MapReduce查询大规模的半结构化数据集. 样例执行的环境为cloudera的单节点虚拟机 读取结构数据中的指定列 在hdfs上放置一个文件 [cl ...
- 一步一步学Python(2) 连接多台主机执行脚本
最近在客户现场,每日都需要巡检大量主机系统的备库信息.如果一台台执行,时间浪费的就太冤枉了. 参考同事之前写的一个python脚本,配合各主机上写好的shell检查脚本,实现一次操作得到所有巡检结果. ...
- Java中String类型的部分用法
1.如何将字符串转换为整型数值? int i = Integer.parseInt("20"); 2.如何用“==”还是equals比较两个字符串? “==”是用来比较俩引用是不是 ...
- linux下keepalived 安装配置
keepalived是一个类似于layer3, 4 & 7交换机制的软件,也就是我们平时说的第3层.第4层和第7层交换.Keepalived的作用是检测web服务器的状态,如果有一台web服务 ...
- Android中使用Handler以及CountDownTimer实现包含倒计时的闪屏页面
上一篇博文<Android中Handler使用浅析>通过实现倒计时闪屏页面的制作引出了Handler的使用方法以及实现原理,博文末尾也提到了实现过程中的Bug,有兴趣的朋友可以点击链接回去 ...
- 文件的上传(表单上传和ajax文件异步上传)
项目中用户上传总是少不了的,下面就主要的列举一下表单上传和ajax上传!注意: context.Request.Files不适合对大文件进行操作,下面列举的主要对于小文件上传的处理! 资源下载: 一. ...
- x86主机搭建家庭智能路由系统 ---- 设计篇
组件简介 Proxmox: 基于Debian的虚拟化系统,支持OpenVZ和KVM. pfSense: 基于FreeBSD的开源路由系统. FreeNAS: 开源NAS系统. shadowsocks: ...