条款26:尽可能延后变量定义式的出现时间

当你定义了一个变量,如果在使用变量之前出现异常,那么你得承受一次构造成本和析构成本,而且你没有使用该变量;本条款给出的建议是延迟变量的定义,直到非得使用该变量的前一刻为止,甚至应该尝试延后这份定义知道能够给它初值实参为止;这样不仅可以避免构造(析构)非必要的对象,还可以避免无意义的default构造行为;

条款27:尽量少做转型动作

常见C风格的类型转换:(T)expression                 函数风格的转型   T(expression)      //旧式转型

C++提供的四种新式转型:

const_cast<T>(expression);//去除对象常量性;

dynamic_cast<T>(expression);//对象安全向下转型,用于继承

reinterpret_cast<T>(expression);

static_cast<T>(expression);//隐式转型

尽量使用新式转型方式:1)代码容易辨识;2)各转型动作的目标比较窄,编译器容易诊断;

转型并不是告诉编译器把某种类型视为另一种类型,任何一个类型转换(不论是通过转型操作而进行的显式转换或通过编译器完成的隐式转换)往往真的令编译器编译出运行期间执行的代码;

对于一个base class指针指向一个derived class对象,有时候上述的两个指针值并不相同,它们之间会有一个偏移量在运行期间施加在derived*指针上来得到正确的base*指针值;转型代码错误分析:

class window{                                       class specialwindow:public window{

public:                                               public:

virtual void onresize( ){...}                              virtual void onresize( ){

}                                                                                 static_cast<window>(*this).onresize( );//解决方法

                                                                                                       //window::onresize();

}

原本想着采用static_cast将派生类对象转化为基类对象执行onresize函数,然后再执行派生类的onresize函数;但实际上是在当前对象的副本上执行了window::onresize,在当前对象上执行是specialwindow专属动作(个人理解:相当于转型可以当做为创建一个副本执行了一次函数调用的过程);

对于dynamic_cast:用于你相对一个derived class执行derived class操作函数,但是你的手上只有一个指向base的pointer或者reference,你只能通过dynamic_cast向下寻找你的derived class对象,然而使用dynamic_cast会用到strcmp比较class名称,使得运行效率极低;改善这个问题的方法有两种:

1)使用容器并在其中存储直接指向derived class对象指针(通常是智能指针),如此便消除了通过base class接口处理对象的需要(但这种情况下,你需要多个容器分别储存不同派生类的指针,而且每个容器都具备类型安全性),具体代码如下:

class window {...};

class specialwindow::public window{

public:

void blink( );

};

typedef std::vector<std::trl:shared_ptr<specialwindow>vpsw;

vpsw winptrs;

for(vpsw::iterator iter=winptrs.begin( );iter!=winptrs.end( );iter++)

(*it)->blink( );

2)通过base class接口处理所有可能之各种window派生类,也就是提供virtual函数;

条款28:避免返回handles指向对象内部成分

对于常见的pimpl设计方法(即数据和实现分离),有两个需要注意的地方:1)成员变量的封装性最多等于返回你reference的函数的访问级别;2)const成员函数传出来一个reference,后者(reference)所指数据与对象自身有关联,而它又被储存于对象之外,那么函数调用者可以修改那笔数据;

绝对不要令成员函数返回一个指针指向访问级别较低的成员函数,如果你那么做,后者的访问级别就会被提高到较高者,因为客户可以去的一个指向访问级别更低的函数,然后通过那个指针去调用它;

如果有一个handle(成员函数返回reference,指针或者迭代器)被传出去,那么就可以用这个handle访问对象的数据,对象的封装性也就下降了;

避免返回handles(reference,指针,迭代器)指向对象内部,遵守这个规定,可以增加封装性,帮助const成员函数更像const,并将发生“虚吊号码牌(指针或引用指向不存在的对象)”的可能性降到最低;

条款29:为“异常安全”而努力是值得的

当异常抛出时,带有异常安全性的函数会:1)不泄露资源;2)不允许数据败坏;

异常安全函数提供一下三个保证中的一个:1)基本承诺;异常被抛出,程序内的任何事情仍然保持在有效的状态下,没有任何对象或者数据结构会因此而败坏,所有对象都处于一种内部前后一致的状态;2)强烈保证:如果异常被抛出,程序状态不改变。调用这样的函数需有这样的认知,如果函数成功,就是完全成功,如果函数失败,程序会回复到“调用函数之前的状态”,调用一个提供强烈保证的函数后,程序状态只有两种可能,如预期般到达函数成功执行后的状态或者回到函数被调用前的状态。3)不抛掷保证,承诺不抛掷异常,因为它们总能完成它们原先承诺的功能;

强烈保证往往能够以copy-and-swap实现出来,但是强烈保证并非对所有的函数都有实现或者具备现实意义,一般基本承诺就可以了;

函数提供的“异常安全性”通常最高只等于其所调用之各个函数的“异常安全性保证”中的最弱者。

条款31:透彻了解inlining的里里外外

inlining函数的好处:产生较小的目标码,调用它们不需要承受函数调用所招致的额外开销;缺点是会导致程序体积过大(虚内存现象)inline造成代码膨胀导致额外的换页行为,降低高速缓存器装置的击中率;

inline只是对编译器的一个申请,不是强制命令,编译器可以忽略;这项申请可以隐喻提出,也可以明确指出(加inline关键字)。隐喻的方式是将函数定义在class定义式内;

inline函数通常被放置在头文件中,在编译阶段实现对函数本体的替换;编译器拒绝将复杂(循环或者递归)、virtual函数函数指针调用的情况下将函数变成inline;

inline函数在随着程序库的升级时无法升级,同时不支持设置端点调试(本身不是函数);

结论:将大多数inline函数限制在小型、被频繁调用的函数身上,这可以使得日后调试过程和二进制升级更容易,也可以使得潜在的代码膨胀最小化,使程序的速度提升最大化;

不要只因为function templates出现在头文件,将它们声明为inline;

条款31:将文件间的编译依存关系降至最低

在设计对象的过程中,我们可以将对象分割为两个class,一个只提供接口(数据),一个负责实现该接口(函数实现),这就是所谓的pimpl方式;这种设计方式的好处是:1)在修改数据代码时,我们只需要修改接口数据就行,而不要修改实现;2)在函数声明时,当其中有class时,我们只需要声明class而不需要定义class;具体设计策略如下:

如果使用object reference或者object pointers可以完成任务,就不要使用objects;

如果能够,尽量以class声明式替换class定义式,这样可以省去调用构造函数的开销;

为声明式和定义式提供不同的头文件;

上述的方法称为handle class 的构造方法,常见的handle class的构造方法除了有pimpl方式,还有一种特殊的抽象基类(interface class),这种class的目的是调试derived class 接口,因此通常不带成员变量,没有构造函数,只有一个虚析构函数和一组pure virtual函数,如:

class person{

public:

virtual ~person( );

virtual std::string name( ) const=0;

virtual std::string birthdata( ) const=0;

virtual std::string address( )const=0;

};

interface class自己创建单个对象,这样的函数称为factory函数或virtual构造函数,它们返回指针(智能指针),指向动态分配所得对象,而该对象支持interface class接口,通常函数被声明为static;代码如下:

class person{

public:

static std::trl::shared_ptr<person>(create(const std::string&name,const Date&birthday,const address&addr);

};

Handle class和interface class解除了接口和实现之间的耦合关系,从而降低文件间的编译依存性;对于handle class,成员函数必须通过impl pointer取得对象数据,每一次访问就会增加一层间接性,同时每一个对象消耗的内存数量必须增加impl pointer的大小,最后impl pointer必须初始化,指向一个动态分配的impl object,因此可能会带来bad alloc异常;interface class每次调用只付出一个间接跳跃成本,此外派生的interface class中必须含有一个vptr,这个指针会增加存放对象所需的内存数量。

在程序开发中,如果使用handle class和interface class的实现码在速度或大小差异大于类之间的耦合时,可以用具体类代替handle class和interface class;

Effective C++学习第八天相关推荐

  1. Vue 学习第八天

    Vue  学习第八天 1. 了解了回掉函数的使用,了解了文件的读取, 2.Promise 函数讲解 console.dir(Promise) //Promise 函数讲解 //1.其是一个构造函数,既 ...

  2. jQuery框架学习第八天:ASP.NET jQuery实施方案

    jQuery框架学习第一天:开始认识jQuery jQuery框架学习第二天:jQuery中万能的选择器 jQuery框架学习第三天:如何管理jQuery包装集 jQuery框架学习第四天:使用jQu ...

  3. python学习第八天---用Django框架创建可重用注册登录系统

    python学习第八天---用Django框架创建可重用注册登录系统 实现的功能 项目开始前的整体规划 搭建环境完成基础配置 创建app 更改语言和时区 数据库表生成 启动开发服务器 浏览器访问,检测 ...

  4. Effective C++ 学习笔记 第七章:模板与泛型编程

    第一章见 Effective C++ 学习笔记 第一章:让自己习惯 C++ 第二章见 Effective C++ 学习笔记 第二章:构造.析构.赋值运算 第三章见 Effective C++ 学习笔记 ...

  5. Java核心技术学习--第八天

    Java核心技术学习--第八天 第九章 集合 算法 排序与混排 二分查找 简单算法 批操作 集合与数组的转换 编写自己的算法 遗留的集合 Hashtable类 枚举 属性映射 栈 位集 第十章 图形程 ...

  6. go语言学习第八天==》mysql数据库增删改查、用go语言 客户端(client)发起htttp get请求,post请求,postForm请求,Head请求,Do请求

    go语言学习第八天==>mysql数据库增删改查.用go语言写 客户端(client)发起htttp get请求,post请求,postForm请求,Head请求,Do请求 引包 import的 ...

  7. effective c++ 学习

    <Effective C++> 目录: 转自:http://blog.csdn.net/KangRoger/article/details/44706403 目录 条款1:视C++为一个语 ...

  8. 51单片机入门学习 第八天

    提示:今天是学习51单片机的第八天,以下就是今天的笔记(我买的是普中的开发板,学习笔记是根据普中的视频进行学习)今天学习的内容是中断和串口通信. 目录 中断系统 1.中断 1.1中断介绍 1.2中断结 ...

  9. angularjs学习第八天笔记(指令作用域研究)

    您好,在前两天对指令的简单了解和系统指令学习后 今天主要研究其指针作用域的相关事情 每一个指令在创建时,其实就构成了自己的一个小的模块单元. 其对于的模块单元都有着其对于的作用域,其中作用域一般有两种 ...

最新文章

  1. 把视频玩出花的快手来到CVPR ,解密背后AI能力,落地空间有多大?
  2. 认清楚服务器的真正身份--深入ARP工作原理
  3. tp5模板 使用php代码,thinkPHP的Html模板标签使用方法
  4. 阿里开发者招聘节 | 2019阿里巴巴技术面试题分享:20位专家28道题
  5. 【规范】前端编码规范——css 规范
  6. 一起学习C语言:C语言基本语法(五)
  7. java构建工具 gradle_Java构建工具
  8. java-io流理解
  9. CCF NOI1049 旋转图像
  10. ATOM Z3000 家族整理之基本信息(一) V2.0版
  11. python刷步数程序设计_【云函数 小米运动刷步数】云函数刷步数
  12. 解决ping/telnet本地localhost可以通过 ping/telnet本地ip无法通过问题
  13. 前端制作PUBG瞄准镜!
  14. Yii Framework 开发教程(30) Zii组件-ListView 示例
  15. CAN笔记(17) 预定义报文ID
  16. Win 98系统启动过程全揭密
  17. R笔记:全子集回归 | 最优子集筛选变量挑选
  18. 【学习笔记】React+React全家桶学习笔记
  19. 导出带有合计行的excel
  20. 前端常见问题以及处理方式 - - - (二)前端路由的两种模式和区别

热门文章

  1. SpringBoot 参数符号转义,用这个包下面的类
  2. SDHelper module加密系统解密
  3. Vs Code 配置C/C++ 开发环境
  4. SD/MMC相关寄存器的介绍
  5. 查找出系统中大于50k 且小于100k 的文件并删除。
  6. 即时聊天IM之二 openfire 整合现有系统用户
  7. SDK 操作 list-view control 实例 -- 遍历进程
  8. Windows Phone 内容滑动切换实现
  9. C++中的指针与引用(转)
  10. 硬件信息修改大师_零成本学习之单片机硬件开发(1)