前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >More Effective C++:35个改善编程与设计的有效方法

More Effective C++:35个改善编程与设计的有效方法

原创
作者头像
qiqi_fu
发布2021-12-02 16:59:32
6600
发布2021-12-02 16:59:32
举报
文章被收录于专栏:CQ品势CQ品势

条款 1:仔细区别 pointers和 references

当你知道你需要指向某个东西,而且绝不会改变指向其他东西,或是当你实现一个操作符而其语法需求无法由 pointers 达成,你就应该选择 references。任何其他时候,请采用pointers。

条款 2:最好使用 C++转型操作符

static_cast 基本上拥有与 C 旧式转型相同的威力与意义,以及相同的限制。

const_cast 最常见的用途就是将某个对象的常量性去除掉。

dynamic_cast 只能用来协助你巡航于继承体系之中

reinterpret_cast这个操作符的转换结果几乎总是与编译平台息息相关。所以 reinterpret_casts 不具移植性。最常用用途是转换“函数指针”类型

条款 3:绝对不要以多态(polymorphically)方式处理数组

多态(polymorphism)和指针算术不能混用。数组对象几乎总是会涉及指针的算术运算,所以数组和多态不要混用。

array[i] 其实是一个“指针算术表达式”的简写:它代表的其实是*(array+i)。我们知道,array是个指针,指向数组起始处。array 所指内存和array+i 所指内存两者相距多远?答案是i*sizeof(数组中的对象),因为array[0] 和 array[i] 之间有 i 个对象。

编译器只能识别一个长度,并不能动态判断数组单位大小。删除时也一样(delete[]),若只调用基类的析构,而不会正确调用派生类的析构函数。

条款 4:非必要不提供 default constructor

条款 5:对定制的“类型转换函数”保持警觉

单自变量 constructors :1.用explecit;2.使用代理类嵌套在类中;

隐式类型转换操作符:不要声明这种操作符(operator double(…)),用函数来替代;

条款 6:区别 increment/decrement 操作符的前置(prefix)和后置(postfix)形式

后置操作符,会有个int的参数,且返回const类型;

条款 7:千万不要重载&&,||和,操作符

重载没办法实现原来&& || ,的“骤死式”;

条款 8:了解各种不同意义的 new和 delete

new operator: new string

operator new: operator new(sizeof(string))

placement new: new(buffer) string; -- 在指定的内存上创建对象;

数组的 new operator : eg, new string[10]

条款 9:利用 destructors避免泄漏资源

将“一定得执行的清理代码”移到processAdoptions 函数的某个局部对象的 destructor 内即可。因为局部对象总是会在函数结束时被析构,不论函数如何结束(唯一例外是你调用 longjmp 而结束。

比如智能指针:auto_prt; 或者设计一个类似于智能指针类的自定义类。

条款 10:在 constructors内阻止资源泄漏(resource leak)

即需要考虑在构造函数内,若产生异常时,无法调用析构释放内存;

将所有可能的exceptions 捕捉起来,执行某种清理工作,然后重新抛出 exception,使它继续传播出去;

若在初始化表达式中except,则将new动作封装到Private函数中,并在函数中捕捉异常并释放内存,然后将该函数应用到初始化表达式中。

或者将 theImage 和 theAudioClip 的原始指针类型改为 auto_ptr;

条款 11:禁止异常(exceptions)流出 destructors之外

有两个好理由支持我们“全力阻止 exceptions传出 destructors之外”。第一,它可以避免terminate函数在 exception传播过程的栈展开(stack-unwinding)机制中被调用;第二,它可以协助确保 destructors 完成其应该完成的所有事情。

条款 12:了解“抛出一个 exception”与“传递一个参数”或“调用一个虚函数”之间的差异

“传递对象到函数去,或是以对象调用虚函数”和“将对象抛出成为一个exception”之间,有 3个主要的差异。

  • 第一,exception objects 总是会被复制,如果以 by value方式捕捉,它们甚至被复制两次。至于传递给函数参数的对象则不一定得复制。
  • 第二,“被抛出成为exceptions”的对象,其被允许的类型转换动作,比“被传递到函数去”的对象少。(异常的只允许继承关系的转换和void*类型的转换)
  • 第三,catch 子句以其“出现于源代码的顺序”被编译器检验比对,其中第一个匹配成功者便执行;而当我们以某对象调用一个虚函数,被选中执行的是那个“与对象类型最佳吻合”的函数,不论它是不是源代码所列的第一个。

条款 13:以 by reference方式捕捉 exceptions

  • 如果 catch by reference,你就可以避开对象删除问题——它会让你动辄得咎,做也不是,不做也不是;你也可以避开 exception objects 的切割(slicing)问题;你可以保留捕捉标准 exceptions 的能力;你也约束了 exception objects 需被复制的次数。

条款 14:明智运用 exception specifications

  • 函数后,加上 throw(),表示不抛出任何异常;throw(int)表示抛出int类型的异常。
  • unexpected的默认行为是调用 terminate,而terminate的默认行为是调用 abort,所以程序如果违反 exception specification,默认结果就是程序被中止。
  • 不应该将 templates 和 exceptionspecifications 混合使用。
  • 如果 A 函数内调用了 B 函数,而B 函数无 exceptionspecifications,那么 A 函数本身也不要设定 exception specifications。

条款 15:了解异常处理(exception handling)的成本

如果使用 try 语句块,代码大约整体膨胀 5%~10%,执行速度亦大约下降这个数。

一个exception specification 通常会招致与 try 语句块相同的成本。

和正常的函数返回动作比较,由于抛出exception 而导致的函数返回,其速度可能比正常情况下慢 3个数量级。这可是大冲击。但是只有在抛出 exception 时你才需要承受这样的冲击,而 exceptions 的出现应该是罕见的。

请将你对 try 语句块和 exceptionspecifications 的使用限制于非用不可的地点,并且在真正异常的情况下才抛出 exceptions。

条款 16:谨记 80-20 法则

80-20 法则说:一个程序 80%的资源用于 20%的代码身上。是的,80%的执行时间花在大约 20%的代码身上,80%的内存被大约 20%的代码使用,80%的磁盘访问动作由 20%的代码执行,80%的维护力气花在 20%的代码上面。

条款 17:考虑使用 lazy evaluation(缓式评估)

条款 18:分期摊还预期的计算成本

条款 19:了解临时对象的来源

结论是:临时对象可能很耗成本,所以你应该尽可能消除它们。然而更重要的是,如何训练出锐利的眼力,看出可能产生临时对象的地方。任何时候只要你看到一个 reference-to-const 参数,就极可能会有一个临时对象被产生出来绑定至该参数上。任何时候只要你看到函数返回一个对象,就会产生临时对象(并于稍后销毁)。学习找出这些架构,你对幕后成本(编译器行为)的洞察力将会有显著地提升。

条款 20:协助完成“返回值优化(RVO)”

条款 21:利用重载技术(overload)避免隐式类型转换(implicittype conversions)

条款 22:考虑以操作符复合形式(op=)取代其独身形式(op)

条款 23:考虑使用其他程序库

条款 24:了解 virtual functions、multiple inheritance、virtualbase classes、runtime type identification的成本

条款 25:将 constructor和 non-member functions虚化

条款 26:限制某个 class所能产生的对象数量

条款 27:要求(或禁止)对象产生于 heap之中

条款 28:Smart Pointers(智能指针)

条款 29:Reference counting(引用计数)

条款 30:Proxy classes(替身类、代理类)

条款 31:让函数根据一个以上的对象类型来决定如何虚化

条款 32:在未来时态下发展程序

条款 33:将非尾端类(non~leaf classes)设计为抽象类(abstract classes)

条款 34:如何在同一个程序中结合 C++和 C

如果你打算在同一个程序中混用 C++和 C,请记住以下几个简单守则:

● 确定你的 C++和 C 编译器产出兼容的目标文件(object files)。

● 将双方都使用的函数声明为 extern "C"。

● 如果可能,尽量在 C++中撰写 main。

● 总是以 delete删除 new返回的内存;总是以 free 释放 malloc 返回的内存。

● 将两个语言间的“数据结构传递”限制于 C 所能了解的形式;C++structs 如果内含非虚函数,倒是不受此限。

条款 35:让自己习惯于标准 C++语言

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档