Effective C++ 38-42
38.绝不要又一次定义继承而来的缺省參数值。
又一次定义函数缺省參数值意味着又一次定义函数。而非虚函数不能又一次定义,所以将就考虑不能又一次定义虚函数的缺省參数值的原因:虚函数是动态绑定的而缺省參数值是静态绑定的。
静态类型是指程序中声明的类型,而动态类型是指实际对象的类型。举个栗子:
class A{
public:
virtual void fun(int a=0) const{cout<<a<<endl;}
}; class B:public A{
public:
virtual void fun(int a =2)const{cout<<a<<endl;}
};
int main(){
B* pb = new B();//pb的静态类型为 B*
A* pa = pb;//pa 的静态类型 为 A*,
//可是一个指针的静态类型不一定为其动态类型,如pa它的动态类型却是B类型的对象,这是由动态绑定实现的
pb->fun();
pa->fun();
虚函数是动态绑定的。但缺省參数值是静态绑定的,即对于pb,pa调用的虚函数,其使用的默认參数值都为静态绑定的,pa绑定的是 A类中的 a= 0,而pb绑定的是B类中的 a=2,两者不同。尽管函数都是调用B中动态绑定的虚函数,可是默认參数不同,输出结果也不同。
39.避免 ”向下转换“ 继承层次。
从一个基类指针到一个派生类指针称为向下转换,一般使用static_cast将基类指针强制转换为派生类指针。向下转换难看,easy导致错误。且难以理解,升级和维护。
向下转换的消除:使用虚函数调用来取代。第一种方法非常easy理解,可是对于一些类不适用,如基类范围太大导致7有些派生类不应该有这个函数的功能。所以要将这些类的每一个虚函数称为一个空操作。或者作为一个纯虚函数。并默认实现返回错误的操作。第二个方法是加强类型约束,使得指针的声明类型和你所知道的真的指针类型同样。
即对于用到这些向下转换时,通过一些设定。滤去那些不拥有真正指针类型的指针。仅仅留下须要进行操作的指针并以其真实的类型来调用其函数。
假设遇到必需要转换的情况,也不要使用static_cast,而是使用安全的用于多态的向下转换 dynamic_cast,当对一个指针使用dynamic_cast时,先尝试转换。假设成功返回一个合法的指针。否则返回空指针。
40.通过分层来体现”有一个“或”用..来实现“。
使某个类的对象成为还有一个类的数据成员。从而实现将一个类构筑在还有一个类之上,这个称为分层 Layering,也被称为构成,包括或嵌入。
对于有一个的概念非常easy理解,对于一个 Person, 有 Name,Address,Phone等属性,可是不能说Person是一个Name。
对于"用..来实现”,事实上就是调用其他类的对象作为类的主要数据成员,使用这个类的的函数接口来实现新的类中的功能与接口。
41.区分模版和继承。
依据依赖的类的用途来区分,如过依赖的类是类的行为,则为继承,如果依赖的类是类所操作的对象类型,则是模版。
如。企鹅类依赖于鸟类,鸟类中的接口决定的是企鹅类中的行为。即两者是继承关系,而当实现一个集合时,集合类依赖与类T,是因为类T为集合类的进行操作的对象。这是模版。
模版的实现会如果类能够调用T的构造析构赋值等函数。模版的特性是类模版的行为在不论什么地方都不依赖于T。行为不依赖于类型。
当对象的类型不影响类中函数的行为时,就使用模版来生成这样一组类。
当对象的类型影响类中函数的行为时。就用继承来得到这样一组类。
42.明智地使用私有继承。
私有继承不为 “是一个” 的关系。假设两个类之间的继承关系为私有继承,编译器一般不会将派生类对象转换为基类对象。私有继承时,基类的公有和protected 类型的成员都变成派生类的私有成员。私有继承意味着”用...实现“。私有继承纯粹是一种实现 技术。
私有继承仅仅是继承实现,而忽略接口。
私有继承在 软件 ”设计“过程中毫无意义,仅仅是在软件”实现“时才实用。
对于分层,也有 用...实现的含义,对于分层与私有继承,尽可能使用分层。必要时才使用私有继承。
而建议使用私有继承在用到保护成员和有虚函数介入时。
对于一个基类。仅仅作为其它类的实现来使用,使用分层作为其它类的私有成员。但其不是抽象类,导致其可能被其它人任意调用导致出错。这是就须要使用到私有继承。对于这样的具有实现可是仅仅能用于特定用途的基类。将其接口都改为protected类型。而正确使用它的类不用分层而使用私有继承来安全的使用基类。
对于模版,其为C++中最实用的组成部分之中的一个,可是,实例化一个模版,就可能实例化实现这个模版的代码,如构成set<int> 和set<double>的代码是全然分开的两份代码,模版会导致代码膨胀。改进的方法:创建一个通用类,储存对象的void*指针。创建还有一组类来保证类型安全使用通用类。以实现栈stack为例,先构建一个stack的通用类:
class GenericStack{
protected://实现类使用私有继承继承这个通用类,所以将接口保护起来
GenericStack();
~GenericStack();
void push(void* object);//使用指针
void* pop();
bool empty() const;
private:
struct StackNode{
void *data;
StackNode *next;
//在stack中使用指针来传递数据和保存数据,则节点析构时不用释放void指针指向的内存。
StackNode(void *newData,StackNode *nextNode)
:data(newData),next(nextNode){} };
StackNode *top;
GenericStack(const GenericStack&);//防止拷贝和赋值
GenericStack& operator=(const GenericStack&);
};
而要实现stack的详细类通过私有继承这个类来实现功能,并且能够使用模版来完美的完毕这个工作:
template <class T>
class Stack:private GenericStack{
public:
void push(T* objectPtr){GenericStack::push(objectPtr);}
T* pop(){return static_cast<T*>(GenericStack::pop());}
bool empty() const {return GenericStack::empty();}
};
这里使用私有继承。将通用类GenericStatck作为事实上现,而其接口函数都是内联函数,差点儿没有消耗,并且使用模版实现了类型安全的推断。对于创建随意类型的stack仅仅要又一次编译这个三个简单的内联函数就可以,而不是通用类中复杂的实现。极大的减少了程序的开销。
这种代码是令人惊叹的。近乎完美的。
首先使用了模版,编译器会依据你的须要来自己主动生成全部的接口。由于使用模版。这些类是类型安全的,类型错误会在编译期间就能发现。由于GenericStack的成员函数是保护类型,用户不可能绕过接口类来调用它。
由于这个模版的接口成员函数都被隐式的声明为内联。使用这些类时不会带来执行开销,生成代码就想用户直接使用GenericStack来编写的一样。由于GenericStack是使用void*指针,操作栈的代码仅仅须要一份。而不同类型仅仅要简单的编译类模版中的简单的内联函数即可。
简而言之,这个设计使代码达到了最高的效率和最高的类型安全。
Effective C++ 38-42的更多相关文章
- Effective Modern C++ 42 Specific Ways to Improve Your Use of C++11 and C++14
Item 1: Understand template type deduction. Item 2: Understand auto type deduction. Item 3: Understa ...
- Effective C++ -----条款42:了解typename的双重意义
声明template参数时,前缀关键字class和typename可互换. 请使用关键字typename标识嵌套从属类型名称:但不得在base class lists(基类列)或member init ...
- Effective Java 38 Check parameters for validity
For public methods, use the Javadoc @throws tag to document the exception that will be thrown if a r ...
- 读书笔记 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 42 了解 typename 的双重意义
本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 经验:声明 template 參数时,前缀keyword class 和 typename ...
- Effective C++ 条款42
本节条款我们讨论一下class 关键字和typename关键字的不同以及对于模板函数(template function)的影响. 例如以下代码: template<class T> T ...
- Effective Java Index
Hi guys, I am happy to tell you that I am moving to the open source world. And Java is the 1st langu ...
- AWR Report 关键参数详细分析
WORKLOAD REPOSITORY report for DB Name DB Id Instance Inst num Startup Time Release RAC CALLDB 12510 ...
- Morris.js和flot绘制折线图的比较
[文章摘要] 最近用开源的AdminLTE做框架感觉效果特别好,其针对图表库Morris.js和flot都提供了不错的支持,也都提供了这两者的例子.不过Morris.js是基于Raphael.js来的 ...
随机推荐
- Java_基础知识回顾
1.ByteArrayInputStream. ByteArrayOutputStream String str = "ZHANGSAN"; //System.out.printl ...
- Maven项目META-INF文件夹不存在的问题
直接在resources文件夹下新建META-INF文件夹即可. 参考: https://my.oschina.net/KingPan/blog/750159 https://stackoverflo ...
- MySQL:按前缀批量删除表格
想要实现mysql>drop table like "prefix_%" 没有直接可用的命令,不过可以通过mysql语法来组装, SELECT CONCAT( 'DROP T ...
- linux shell if语句使用方法
原文地址:http://blog.chinaunix.net/uid-24607609-id-2118151.html 最精简的 if 命令的语法是: if TEST-COMMANDS; then C ...
- 栅栏加解密python实现(支持密钥加密)
栅栏加解密是对较短字符串的一种处理方式.给定行数Row,依据字符串长度计算出列数Column,构成一个方阵. 加密过程:就是按列依次从上到下对明文进行排列,然后依照密钥对各行进行打乱.最后以行顺序从左 ...
- C++中模板单例的跨SO(DLL)问题:RTTI,typeid,static,单例
(转载请注明原创于潘多拉盒子) C++的模板可以帮助我们编写适合不同类型的模板类,给代码的复用性提供了极大的方便.近来写了一个涉及单例的C++模板类,简化下来可以归结为以下的代码: template ...
- 关于 json 单引号和双引号区别--请使用双引号
双引号才是json 的标准,单引号是不规范的(虽然在js 中是行的通的)! 由于某些原因,在将js json数据传到as 中处理的时候出现了“无效的json解析输入” ,于是查找了一下js参数是否有问 ...
- 《OpenStack部署实践》
<OpenStack部署实践> 基本信息 作者: 张子凡 丛书名: 图灵原创 出版社:人民邮电出版社 ISBN:9787115346797 上架时间:2014-2-27 出版日期:2014 ...
- HTTP协议状态码详解(HTTP Status Code)(转)
原文链接:HTTP协议状态码详解(HTTP Status Code) 使用ASP.NET/PHP/JSP 或者javascript都会用到http的不同状态,一些常见的状态码为: 200 – 服务器成 ...
- html调用servlet(JDBC在Servlet中的使用)(2)
5.修改数据 5.1编写查询条件页面 修改单条数据的时候,首先是查询出单个数据的详细信息,然后根据实际需要部分修改或者全部修改.修改之后,数据会提交到数据库,数据库中保存更新以后的数据. 查询出单条数 ...