总结: 囫囵吞枣地看完了EffectiveC++这本书,收获不少。收获主要是以下几个方面:
编程注意细节 提高程序效率的细节比如const
is-a 和 has-a的区别pure-virtual、impure-virtual和non-virtual之间继承该如何处理继承中隐藏基类的成员等设计方法 什么时候该继承(is-a),什么时候该以对象作为data member(is-inplemented-in-terms-of);Template method、 Strategy, 抽象工厂等设计模式泛型编程与模板编程异常安全性 异常处理copy-swap技术由于没有太多的实战经历,所以有些东西并不是很懂。 所幸在看书过程中,做过一些笔记整理,其中 大部分内容摘抄自书上,一小部分加上自己的一点点理解,方便日后回顾。
02: 对于单纯常量,最好以const
对象或 enums
替换#define
03: 尽可能使用const
mutable
关键字表征 const
成员函数可以修改mutable
成员04: 为内置对象进行手工初始化,C++并不保证初始化他们;
05: 在类中,如果有引用 或const对象,记得要自己编写构造函数,拷贝构造函数等,因为编译器自己生成的相关函数很大可能达不到要求。
06: 为了驳回编译器自动提供的功能(构造函数,拷贝等),可以将相应的member function declared PRivate,并且不予实现; 或者使用像Uncopyable这样的base class;
07: 当类作为基类的时候,有可能表现出多态的时候,应将虚构函数设计成virtual
09: 绝不在构造函数和析构函数中调用virtual function; 如果在base class 调用了virtual function,在派生类的构造函数初始化基类的时候,实际上还是调用的是base class的function,并没有调用dervied class的内容(此时derived class 并没有完成初始化)
10: 令 Operator= 返回一个referece to *this (方便连续赋值 如 a = b = c;);
11: 在operator=中处理“自我赋值”copy and swap 技术实现自我赋值;
class Widgt {void swap(Widgt& rhs);// 交换*this 和 rhs的数据}Widget& Widget::operator=(const Widget& rhs) { Widget tmp(rhs); swap(rhs); return *this;}12: 复制对象的时候务必记住每一个成员 copying函数应该确保复制“对象内的所有成员变量” 以及“所有base Class 的成分”
13: 以对象管理资源 记得用智能指针管理资源;
14: 在资源管理类中心小心copying行为
当资源管理类需要copy的时候 需要从以下几个方面考虑 禁止复制(将拷贝构造函数,以及拷贝赋值函数 设置于private有点类似于unique_ptr<>)对底层资源使用“引用计数法” 模仿“shared_ptr”复制底部资源(深拷贝,每一份对象都有自己的heap内存)转移资源的管理权(有点像 auto_ptr )15: 资源管理类中需要提供对原始资源的访问
智能指针类可以隐式转化为底部的原始指针,便于访问底部的成员可以有显式转换(较为安全)和隐式转换(对客户比较安全)16: 成对使用new 和delete 尤其注意在利用typedef中声明新的类型名时(尽量少用typedef声明数组类型)
17: 以独立语句将new出来的对象置入智能指针25 : Consider support for a non-throwing swap( 不抛异常)
缺省版本swap如果缺省版本的效率不够,可以尝试做
提供一个public swap函数(这个函数,不要抛出异常)在class 或template所在的命名空间内提供一个non-member swap,并调用上述的swap成员函数如果正编写一个class(非template),需要特化std::swap, 并令他调用你的成员swap注意: 不要忘std里加入任何新的东西
// default swaptemplate <typename T>void swap(T& a, T& b){ T temp(a); a = b; b = tmp;}class Widget { public: void swap(Widget &other);}26: 尽可能延后变量定义式的出现的时间(用到的时候才再去声明) 循环里用到的变量尽量在循环里声明,除非 赋值成本比构造加析构低,处理代码中效率高度敏感的部分27: 尽量少做转型动作(如果转型必要,就封装在函数里面)(新式转型 好于 旧式转型)
const_castdynamic_caststatic_cast reinterpret_cast 不适合移植28: Avoid returning “handles”to object internals
handles (pointer, reference, iterators)29: Strive for exception safety code(为异常安全性努力是值得的)
带有异常安全性的函数会: 不泄露任何资源不允许数据败坏异常安全函数(Exception-safe code)提供下面三个保证之一
基本承诺
程序内的任何事物都保持在有效状态下(对象或者数据结构不会有因此而损坏)当异常时,可以选用默认状态,也可以回复到变换之前的状态强烈保证(往往以copy-and-swap实现出来)
函数成功,就完全成功,如果失败,程序会回复到调用函数之前的状态nothrow 保证30: Understand the ins and outs of inlining
virtual 和inline同时出现,编译器往往会拒绝inlineinline限制在小型、频繁调用的函数身上构造函数最好不要inline31: 将文件间的编译依存关系降至最低
接口与实现的分离 如果使用object reference 或者 object pointers可以完成任务,就不要使用objects如果能够,尽量以class声明式替换定义式为声明式和定义式提供不同的头文件32: 确定public继承是is-a的关系(注意区分has-a)
适用于derived class身上的所有事情都能适用于base class身上33: Avoid hiding inherited names(避免隐藏继承的成员名)
避免隐藏基类的成员名 可以使用using 声明式或者 forwarding function34: Differentiate between inheritance of interface and inheritance of implementation(区分接口继承和实现继承)
pure virtual: 为了让derived class继承函数接口(继承了的具象类都要重新声明)impure virtual: 继承该函数的接口和缺省实现non-virtual:为了令derived classes继承函数的接口及一份强制性实现35: 考虑virtual函数以外的其他设计方式(提及设计模式中的Template Method 和 Strategy)
令客户通过public non-virtual成员函数间接调用private函数(NVI手法)/// Template Methodclass GameCharacter {public: int healthValue() const { ··· // 做一些事前工作 int retVal = doHealthValue(); ··· //做一些事后工作 return value; } ···private: virtual int doHealthValue() const { ··· // 缺省算法,计算健康指数 } }借由function pointers实现strategy模式class GameCharacter;int defaultHealthCalc(const GameCharacter& gc);class GameCharacter {public: typedef std::function<int (const GameCharacter&)> HealthCalcFunc; explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc) : healthFunc(hcf) {} int healthValue() const { return healthFunc(*this);}private: HealthCalcFunc healthFunc;};/// 可以转换成function对象short calcHealth(const GameCharacter&); // 返回类型 non-intstruct HealthCalculator { //为计算健康值设计的函数对象 int operator()(const GameCharacter&) const {···}};class GameLevel {public: float health(const GameCharacter&) const; // 成员函数 ···};使用non-virtual interface手法,那是Template Method设计模式的一种特殊形式,它以public non-virtual成员函数包裹性较低访问性(private或protected)的virtual函数将virtual函数替换为“函数指针成员变量”,这是strategy设计模式的一种分解表现形式以function成员变量替换virtual函数, 因此允许使用任何可以转变成(callable entity)function对象的表达式,也是strategy的一种表现形式36: Never redefine an inherited non-virtual function.
如果重新定义从基类继承的non-virtual function, 那么就应该是设计出现了问题37: 绝不重新定义继承而来的缺省参数值(针对virtual函数)
virtual函数系动态绑定,而缺省参数值却是静态绑定如果需要缺省值,应该将该函数设计为non-virtual函数,再设计一个private的virtual function 如同前面介绍的Template Method设计38: Model “has-a”or “is-implementated-in-terms-of” through composition
在应用域内,是has-a的关系在实现域中,是根据某物实现出 例如STL库中的stack是以vector为底层数据结构实现的39: 明智而谨慎地使用private继承
private继承某种意义上说 is-implemented-in-terms-of, 所以尽可能使用38条中使用复合的方式实现除非确定private继承可以造成empty base最优化40: 明智而谨慎地使用多重继承
多重继承比单一继承复杂;以及可能会对virtual继承的需要virtual继承会增加大小,速度,初始化复杂度等成本41: 了解隐式接口和编译器多态
以不同的template参数具现化function templates会导致调用不同的函数,这就是编译期多态(有点像重载函数) classes 和 templates都支持接口(interface)和多态(polymorphism)对classes而言接口是显式的,以函数签名为中心。多态则是通过virtual函数发生于运行期对template参数而言,接口是隐式(implicit)的,奠基于有效表达式;多态则是通过template具现化发生于编译期42: 了解typename的双重意义
声明template参数时,class和typename可以互换记得使用typename标识嵌套从属类型名称;但不要在base class List或 member initialization list内以它作为base class的修饰符43: 学习处理模板化基类内的名称
继承模板基类的时候,基类的成员函数不可见;这个时候可以用一下三种方式解决 在前面添加this指针利用using语句,使模板基类的函数可见利用基类的类名加::调用基类函数44: 将与参数无关的代码抽离template
template生成多个classes和多个function,所以任何template代码都不该与某个造成膨胀(怎么造成代码膨胀呢?)的template参数产生相依关系因非类型模板参数而造成的代码膨胀,往往可以消除,做法是以函数参数或class成员变量替换template参数因类型参数(type parameters)而造成的代码膨胀,往往可以降低,做法是让带有王权相同的二进制表述的instantiation types共享实现码45: 运用成员函数模板接受所有的兼容类型
请使用member function templates生成“可接受所有兼容类型”的函数如果你声明member templates 用于“泛化copy构造”或泛化assignment操作,还是需要声明正常的拷贝构造函数和copy assignment操作符46: 需要类型转换时请为模板定义非成员函数 编写class template,需要支持与此template相关函数 支持参数之隐式类型转换 时,请将那些函数定义为 class template内部的friend函数47: 请使用traits classes表现类型信息
Trait classes使得“类型相关信息”在编译器可以用,以template特化实现整合overloading技术48: 认识template元编程
49: 了解new-handler的行为
new-handler函数要完成以下的事情:
让更多内存可被使用安装另一个new-handler(令new-handler修改会影响new-handler行为的static数据,global数据)卸除new-handler(将null指针传递给set_new_handler)跑出bad_alloc(或派生出来的)异常Nothrow new是一个颇为局限的工具,因为它只适用于内存分配 够来的构造函数还是可能跑出异常
50: 了解new和delete的合理替换时机
51: 编写new和delete时需要固守常规
52: 写了placement new也要写placement delete(不熟悉)
53: pay attention to compiler warnings
编译器的警告能力是不一样的; 编写程序,争取完全无警告54: 让自己熟悉TR1在内的标准程序库(现在C++11基本都实现了)
55: 让自己熟悉boost程序库(泛型编程, 模板元编程)
没有理解和不太懂的地方,也有几处,主要集中于:
泛型编程模板元编程trait type、trait class, 特化以及偏特化接下来的学习之路也需要偏重以下上述方面。
新闻热点
疑难解答
图片精选