Effective C++学习第十一天
条款41:了解隐式接口和编译期多态
面向对象编程世界总是以显式接口(源码可见的接口)和运行期多态(virtual)解决问题;对于templates及泛型编程的世界,隐式接口和编译期多态显得更加重要;
对于template,只有当参数具体确定时(具现化)才能确定具体操作,而这些具现化的行为发生在编译期,以不同的template参数具现化function templates会导致调用不同的函数,这就是所谓的编译期多态。
通常显示接口由函数的签名式(函数名称,参数类型,返回类型)构成,而隐式接口则不基于函数签名式,它是由有效表达式组成,表达式自身看起来很复杂,但它们要求的约束条件一般而言直接而又明确;
template参数身上的隐式接口,和class对象上的显示接口,都是在编译器完成检查;
对template参数而言,接口是隐式的,基于有效表达式;多态则是通过template具现化和函数重载解析发生于编译器;
条款42:了解typename的双重意义
在声明template类型参数时,class和typename意义完全相同;
引申:template内出现的名称如果相依于某个template参数,称之为从属名称,如果从属名称在class内呈嵌套状,我们称它为嵌套从属名称,如c::const_iterator就是这样的名称,它其实是一个嵌套从属类型名称,也就是个嵌套从属名称并指涉某类型;如果一个名称(int)并不依赖于template参数的名称,这样的名称称为谓非从属名称;代码如下:
template<typename c>
void print2nd(const c&container)
{
if(container.size()>=2){
c::const_iterator iter(container.begin( ));
++iter;
int value=*iter;
}
}
嵌套从属名称会导致编译困难,如c::const_iterator *p;C++解析代码有一个规定:如果解析器在template中遭遇一个嵌套从属名称,它便假设这个名称不是个类型,除非你告诉它是,所以缺省情况下,嵌套从属名称不是类型(有一个例外);正确的情况就是告诉C++解析器这个一个类型,在名称前加关键字typename就可以了;
typename只被用来验明嵌套从属类型名称,其他名称不该有它的存在;typename作为嵌套从属类型名称的前缀词有一个例外:typename不可以出现在base classes list内的嵌套从属结构,也不可在member initialization list(成员初始列表)中作为base class修饰符,如:
template <typename T>
class derived:public base<T>::Nested{
public:
explicit derived(int x):base<T>::Nested(x){
typename base<T>::Nested temp;
}
};
条款43:学习处理模块化基类内的名称
考虑如下的程序代码:
template <typename company> template<typename company>
class msgsender{ class loggingmsgsender:public msgsender<company>{
public: public:
void sendclear(const msginfo & info) void sendclearmsg(const msginfo & info){
{
std::string msg; sendclear(info);//调用base class函数,代码编译不通过
company c;
c.sendcleartext(msg); }
} };
};
分析:当编译器遇到class template loggingmsgsender定义式,并不知道它继承什么样的class,当然它继承的是msgsender<company>,但其中的company是个template参数,不到后来(loggingmsgsender被具现化)无法确切知道它是什么,也就不知道是否有sendclear这个函数;C++编译器拒绝调用sendclear的原因:它知道base classtemplates有可能被特化,而那个特化的版本不提供和一般性template相同的接口;
解决C++不进入templatized base classes的方法有三种:1)在base class函数调用之前加上this->,也就是this->sendclear(info);2)使用using声明式,告诉编译器sendclear在base class内,如using msgsender<company>::sendclear;3)明确指出被调用函数位于base class内,如msgsender<company>::sendclear(info);但是如果调用的是virtual函数,那么就会关闭virtual绑定行为;
条款44:将与参数无关的代码抽离templates
templates是节省时间和避免代码重复的一个奇方妙法,但是使用templates可能会导致代码膨胀,其二进制码带着重复的代码数据;可以通过使用共性和变性分析的方法解决这个问题,将多个class的共同部分搬离到新的class,并通过继承或复合的方式来得到公共属性;
任何templates代码都不该与某个造成膨胀的templates参数产生相依的关系;
因非类型模板参数而造成的代码膨胀,往往可消除,做法是以函数参数或class成员变量替换templates参数,如矩阵类求逆实现;
因类型参数而造成的代码膨胀,往往可以降低,做法是让带有完全相同二进制表述的具现类型共享实现代码,如int和long实现;
如果你实现某些成员函数而他们操作强型指针(如T*),你应该令他们调用另一个操作无类型指针(void *)的函数,后者完成实际工作;
条款45:运用成员函数模板接受所有兼容类型
真实指针支持隐式转换,如derived class隐式转换成base class;但是同一个templates的不同具现体之间并不存在什么与生俱来的固有关系(如某个带有base-derived关系的B,D两类型分别具现某个template,产生的两个具现体并不带有base-derived关系);
C++给我们提供一个称为member function templates,它为模板类提供构造函数;这一类构造函数通过对象u创建对象t,而u和t的类型是同一个template的不同具现体,我们称这个函数为泛化构造函数,如:
template<typename T>
class smartptr{
public:
template<typename u>
smartptr(const smartptr<u>& other);
};
但是对于泛化构造函数,有时我们需要指定隐式转换的方向,例如我们不能将int *转换为double*,这时候我们可能需要一个指针指向class的原始数据,这样问题就变成只有存在两个底层指针可以转换的才是我们需要的结果;
member function templates(成员函数模板)的效果不限于构造函数,它还支持赋值操作,如shared_ptr类的代码:
template<typename T>
class shared_ptr{
public:
template<class Y>
explicit shared_ptr(Y* p);//没有const,原因???
template<class Y>
shared_ptr(shared_ptr<Y>const&r);//没有explicit,允许隐式转换
template<class Y>
explicit shared_ptr(weak_ptr<Y>const&r);
template<class Y>
explicit shared_ptr(auto_ptr<Y>&r);//没有const,auto_ptr性质决定
template<class Y>
shared_ptr&operator=(shared_ptr<Y>const &r);
template<class Y>
shared_ptr&operator=(auto_ptr<Y>&r);//没有const,auto_ptr性质决定
};
member function templates会声明一个泛化的copy构造函数和copy assignment操作符,但是它不会改变C++语言的规则,C++语言规定如果程序需要一个copy构造函数,你没有声明,编译器会自动帮你生成一个,相同规则也适用于赋值操作,因此如果你不想让系统声明正常的拷贝构造函数和赋值操作符,你得自己声明;
条款46:需要类型转换时请为模板定义非成员函数
当不存在模板时,如果函数参数都存在隐式转换,则需要将这个函数定义成非成员函数;当存在模板时,这条规则又发生了变化,编译无法通过,因为在template具现化实参推导过程中从不将隐式类型转换函数纳入考虑;
解决的方法:利用friend关键字,将非成员函数声明为友元函数(新用法),同时提供实现(实现在函数外,通过友元函数调用,这样可以实现代码冲击最小化)(如果只是单纯提供友元声明,那么可以通过编译,却没有办法完成连接),这种称为混合式代码调用,代码调用成功的原因是:函数在被调用的过程中参数可以实现隐式变换;
类模板的声明式为:template<typename T> class rational;
Effective C++学习第十一天相关推荐
- 一步步学习SPD2010--第十一章节--处理母版页(8)--从母版页创建网页
一步步学习SPD2010--第十一章节--处理母版页(8)--从母版页创建网页 在SPD中,你可以使用许多不同的方法创建ASP.NET页面.然而,因为页面将是SP网站的一部分,你很可能想像其他页面一样 ...
- 一步步学习SPD2010--第十一章节--处理母版页(7)--管理Content Placeholders
一步步学习SPD2010--第十一章节--处理母版页(7)--管理Content Placeholders 正如我之前描述的,母版页在你的网站页面间,用来保持一致的外观.然而,母版页还有其他特殊的功能 ...
- 一步步学习SPD2010--第十一章节--处理母版页(10)--重置母版页到网站定义
一步步学习SPD2010--第十一章节--处理母版页(10)--重置母版页到网站定义 在第一章节,你将内容页重置为网站定义.重置母版页到网站定义也没有什么不同.你丢失了在页面上做出的自定义,包括任何静 ...
- Apache Nutch 1.3 学习笔记十一(页面评分机制 OPIC)
1. Nutch 1.3 的页面评分机制 Nutch1.3目前默认还是使用OPIC作为其网页分数算法,但其之后,已经引入了PageRank-like算法,以弥补OPIC算法的不足,目前OPIC算法还是 ...
- 从零开始学习jQuery (十一) 实战表单验证与自动完成提示插件
本系列文章导航 从零开始学习jQuery (一) 开天辟地入门篇 从零开始学习jQuery (二) 万能的选择器 从零开始学习jQuery (三) 管理jQuery包装集 从零开始学习jQuery ( ...
- OpenCV学习(二十一) :计算图像连通分量:connectedComponents(),connectedComponentsWithStats()
OpenCV学习(二十一) :计算图像连通分量:connectedComponents(),connectedComponentsWithStats() 1.connectedComponents() ...
- 吴恩达《机器学习》学习笔记十一——应用机器学习的建议
吴恩达<机器学习>学习笔记十一--应用机器学习的建议 一.训练完模型后下一步要做什么 二.评估算法与模型选择 1.训练集与测试集 2.训练/测试步骤 3.模型选择 4.数据集新的划分--验 ...
- 吴恩达《机器学习》学习笔记十一——神经网络代码
吴恩达<机器学习>学习笔记十一--神经网络代码 数据准备 神经网络结构与代价函数· 初始化设置 反向传播算法 训练网络与验证 课程链接:https://www.bilibili.com/v ...
- jQuery框架学习第十一天:实战jQuery表单验证及jQuery自动完成提示插件
jQuery框架学习第一天:开始认识jQuery jQuery框架学习第二天:jQuery中万能的选择器 jQuery框架学习第三天:如何管理jQuery包装集 jQuery框架学习第四天:使用jQ ...
最新文章
- Android开发者指南(22) —— Accessing Resources
- 2018.08.10 atcoder Median Sum(01背包)
- C++内存泄露检测原理
- python中 for ... else ... 的用法
- datagrid出现相同两组数据_多数据库联用挖掘肝细胞癌症的血管侵犯特征基因
- nginx+keepalived详细配置信息
- 辣味就直往眼睛的专业o2o资讯
- (第一周)软件工程四人组
- 数据结构和算法面试题系列—二叉树面试题汇总
- js得到自定义属性和操作table表格
- TCP编程、UDP编程
- GAT GAX 简介
- python合并多个pdf文件
- 哔哩哔哩我来了,see goodbye 马总!!!
- C语言volatile修饰的到底什么鬼?原来它在嵌入式开发是必须掌握的!
- 大数据基本概念hadoop、hadoop生态系统hdfsMapReduce
- 什么是Remoting??
- 数据库之数据库和表的创建
- 机械硬盘大势已去?NAS硬盘笑了
- 硬盘在linux下不认,LINUX不认硬盘!