Effective C++ 读书笔记三
2018-04-07 03:05
483 查看
实现
Item26:尽可能延后变量定义式出现的时间
Item27:尽量少做转型动作
Item28:避免返回handles指向对象内部成分
Item29:为“异常安全”而努力是值得的
Item30:透彻了解inline函数的里里外外
Item31:将文件的编译依存关系降到最低
继承与面对对象设计
Item32:确定你的public继承塑模出is-a模型
Item33:避免遮掩继承而来的名称
Item34:区分接口继承和实现继承
Item35:考虑virtual函数以外的选择
提供非虚接口(模板方法模式)
藉由函数指针实现策略模式
藉由tr1::function实现策略模式
Classic的策略模式
Item36:绝不重新定义继承而来的non-virtual函数
Item37:绝不重新定义继承而来的缺省参数值
Item38:通过复合塑模出has-a或者”根据某物实现出”
Item39:明智而审慎地使用private继承
Item40:明智而审慎地使用多重继承
模板与泛型编程
Item41:了解隐式接口和编译期多态
Item42:了解typename的双重意义
Item43:学习处理模板化基类内的名称
Item44:将参数无关代码抽离template
Item45:运用成员函数模版接收所有兼容类型
Item46:需要类型转换的时请为模版定义非成员函数
Item47:请使用traits classes表现类型信息
Item48:认识模版元编程
杂项讨论
Item53:不要轻易忽略编译器的警告
Item54:让自己熟悉包括TR1在内的标准程序库
Item55:让自己熟悉Boost
总结
清明时节雨纷纷,转眼已经到清明,来杭许久,入职也近两周。上班加班太多,学习时断时续。其实,已经转战客户端开发的我,略显迷茫。这本书啊,明天看完吧!!!
特别是避免使用dynamic_cast,底层实现复杂,效率太慢。可采用下面方式:
或基类声明virtual函数。
调用同名基类方法,如果采用下面的操作只是副本的操作:
static_cast(*this).onResize( );
应该改写为:
Window::onResize();
“异常安全函数”即使发生异常也不会有资源泄漏或允许任何数据结构败坏。在这个基础下,它有3个级别:基本保证、强烈保证、不抛异常。
基本保证:
“强烈保证”往往可以通过copy-and-swap实现:
ps:内置类型和指针的操作就绝不会抛出异常
两个手段:Handle classes(impl对象提供服务)和Interface classes。
To make hidden names visible again, employ using declarations or forwarding functions.
纯虚函数:提供接口继承 Drived class必须实现纯虚函数,但同时可以给纯虚函数提供定义
pure virtual functions must be redeclared in concrete derived classes, but they may also have implementations of their own.
虚函数:提供接口继承和默认的实现继承
非虚函数:提供了接口继承和强制的实现继承
EvilBadGuy ebg1(calcHealth);
EyeCandyCharacter ecc1(HealthCalculator());
GameLevel currentLevel; …
EvilBadGuy ebg2(std::tr1::bind(&GameLevel::health, currentLevel, _1));
本条里实现的Set算是适配器的实现方式吧。
✦ Private inheritance means is-implemented-in-terms of. It’s usually inferior to composition, but it makes sense when a derived class needs access to protected base class members or needs to redefine inherited virtual functions. //访问保护成员或重定义虚函数
✦ Unlike composition, private inheritance can enable the empty base optimization. This can be important for library developers who strive to minimize object sizes. //空白基类最优化
采用组合方式更好:
虚继承来达到子类只有一份基类数据目的(但代价不小)
多重继承使用场景:public继承某个Interface class,private继承某个协助实现的class。
使用typename标识嵌套从属类型名称(但最终解释权属于编译器)
typename C::const_iterator iter(container.begin());
可以采用:
this->sendClear(info); //方法一:使用this
using MsgSender::sendClear; //方法二:使用using
MsgSender::sendClear(info); //方法三:使用作用域运算符
成员函数模板是为了泛化,当“泛化拷贝构造函数”和“泛化的赋值构造”时,仍然需要声明正常的拷贝构造函数和赋值构造函数(不改变C++特性)
但不能在类外直接定义(类型推导不可行),其形式只能是友元的定义和声明在一起,如下:
第五章的实现内容,主要是从高效的角度阐述实现,譬如定义延后、内联、少用转换。其中异常安全值得细读。
第六章偏重于继承的讲解,其实暗带多态。继承和多态是一对难兄难弟,多态其实就是面向接口编程,继承一直为人诟病,组合也早成为其后继者。第35条取代虚函数的方法很好。
第七章看的最慢,也最难懂(我没写过类模板)。感觉还是偏重理论而非工程了,后半段倒是有点意思,譬如第47条。
Item26:尽可能延后变量定义式出现的时间
Item27:尽量少做转型动作
Item28:避免返回handles指向对象内部成分
Item29:为“异常安全”而努力是值得的
Item30:透彻了解inline函数的里里外外
Item31:将文件的编译依存关系降到最低
继承与面对对象设计
Item32:确定你的public继承塑模出is-a模型
Item33:避免遮掩继承而来的名称
Item34:区分接口继承和实现继承
Item35:考虑virtual函数以外的选择
提供非虚接口(模板方法模式)
藉由函数指针实现策略模式
藉由tr1::function实现策略模式
Classic的策略模式
Item36:绝不重新定义继承而来的non-virtual函数
Item37:绝不重新定义继承而来的缺省参数值
Item38:通过复合塑模出has-a或者”根据某物实现出”
Item39:明智而审慎地使用private继承
Item40:明智而审慎地使用多重继承
模板与泛型编程
Item41:了解隐式接口和编译期多态
Item42:了解typename的双重意义
Item43:学习处理模板化基类内的名称
Item44:将参数无关代码抽离template
Item45:运用成员函数模版接收所有兼容类型
Item46:需要类型转换的时请为模版定义非成员函数
Item47:请使用traits classes表现类型信息
Item48:认识模版元编程
杂项讨论
Item53:不要轻易忽略编译器的警告
Item54:让自己熟悉包括TR1在内的标准程序库
Item55:让自己熟悉Boost
总结
清明时节雨纷纷,转眼已经到清明,来杭许久,入职也近两周。上班加班太多,学习时断时续。其实,已经转战客户端开发的我,略显迷茫。这本书啊,明天看完吧!!!
实现
Item26:尽可能延后变量定义式出现的时间
减少不必要的构造、析构(最好定义和赋值放到一块)。Item27:尽量少做转型动作
C++四中转换类型:const_cast<T>(expression) dynamic_cast<T>(expression) reinterpret_cast<T>(expression) static_cast<T>(expression)
特别是避免使用dynamic_cast,底层实现复杂,效率太慢。可采用下面方式:
typedef std::vector<std::tr1::shared_ptr<SpecialWindow> > VPSW; VPSW winPtrs; ... for (VPSW::iterator iter = winPtrs.begin(); iter != winPtrs.end(); ++iter) (*iter)->blink( );
或基类声明virtual函数。
调用同名基类方法,如果采用下面的操作只是副本的操作:
static_cast(*this).onResize( );
应该改写为:
Window::onResize();
Item28:避免返回handles指向对象内部成分
会破坏封装性,也可能导致悬挂指针(悬挂handles)。Item29:为“异常安全”而努力是值得的
异常安全就像怀孕,要么没怀上(不安全),要么怀上了(安全)。“异常安全函数”即使发生异常也不会有资源泄漏或允许任何数据结构败坏。在这个基础下,它有3个级别:基本保证、强烈保证、不抛异常。
基本保证:
class PrettyMenu { ... std::tr1::shared_ptr<Image> bgImage; ... }; void PrettyMenu::changeBackground(std::istream& imgSrc) { Lock ml(&mutex); bgImage.reset(new Image(imgSrc)); ++imageChanges; }
“强烈保证”往往可以通过copy-and-swap实现:
struct PMImpl { std::tr1::shared_ptr<Image> bgImage; int imageChanges; }; class PrettyMenu { ... private: Mutex mutex; std::tr1::shared_ptr<PMImpl> pImpl; };
void PrettyMenu::changeBackground(std::istream& imgSrc) { using std::swap; Lock ml(&mutex); std::tr1::shared_ptr<PMImpl> pNew(new PMImpl(*pImpl)); pNew->bgImage.reset(new Image(imgSrc)); ++pNew->imageChanges; swap(pImpl, pNew); }
ps:内置类型和指针的操作就绝不会抛出异常
Item30:透彻了解inline函数的里里外外
讲解了编译器如何对待内联函数。Item31:将文件的编译依存关系降到最低
相依于声明式,不要相依于定义式。两个手段:Handle classes(impl对象提供服务)和Interface classes。
继承与面对对象设计
Item32:确定你的public继承塑模出is-a模型
public继承意味着is-a。适用于base class身上的每一个函数也一定适用于derived class。Item33:避免遮掩继承而来的名称
Names in derived classes hide names in base classes.To make hidden names visible again, employ using declarations or forwarding functions.
class Drive{ public: using Base::f1; //using 声明式 void f1(int); } class Drive{ public: void f1(){ Base::f1(); //转交函数 } }
Item34:区分接口继承和实现继承
class Shape { public: virtual void draw() const = 0; virtual void error(const std::string& msg); int objectID() const; ... };
纯虚函数:提供接口继承 Drived class必须实现纯虚函数,但同时可以给纯虚函数提供定义
pure virtual functions must be redeclared in concrete derived classes, but they may also have implementations of their own.
虚函数:提供接口继承和默认的实现继承
非虚函数:提供了接口继承和强制的实现继承
Item35:考虑virtual函数以外的选择
提供非虚接口(模板方法模式)
non-virtual interface(NVI)class GameCharacter { public: int healthValue() const { ... //事前工作 int retVal = doHealthValue(); ... //事后工作 return retVal; } ... private: //protected、public亦可 virtual int doHealthValue() const { ... } };
藉由函数指针实现策略模式
class GameCharacter; // forward declaration int defaultHealthCalc(const GameCharacter& gc); class GameCharacter { public: typedef int (*HealthCalcFunc)(const GameCharacter&); explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc) : healthFunc(hcf ) {} int healthValue() const { return healthFunc(*this); } ... private: HealthCalcFunc healthFunc; };
藉由tr1::function实现策略模式
这个东西boost库里也有,C++11也有此特性(陈硕极力推崇之)class GameCharacter { public: typedef std::tr1::function<int (const GameCharacter&)> HealthCalcFunc; explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc) : healthFunc(hcf ) {} int healthValue() const { return healthFunc(*this); } ... private: HealthCalcFunc healthFunc; };
short calcHealth(const GameCharacter&); struct HealthCalculator { int operator()(const GameCharacter&) const { ... } }; class GameLevel { public: float health(const GameCharacter&) const; }; class EvilBadGuy: public GameCharacter { ... }; class EyeCandyCharacter: public GameCharacter { ... };
EvilBadGuy ebg1(calcHealth);
EyeCandyCharacter ecc1(HealthCalculator());
GameLevel currentLevel; …
EvilBadGuy ebg2(std::tr1::bind(&GameLevel::health, currentLevel, _1));
Classic的策略模式
这就是一般书籍上介绍的策略模式。Item36:绝不重新定义继承而来的non-virtual函数
否则何必定义为non-virtual函数。Item37:绝不重新定义继承而来的缺省参数值
缺省参数是静态绑定编译的,也就是说实际上只会用基类的缺省值。Item38:通过复合塑模出has-a或者”根据某物实现出”
组合降低耦合,属于设计原则之一。本条里实现的Set算是适配器的实现方式吧。
Item39:明智而审慎地使用private继承
private继承是”根据某物实现出“class Widget: private Timer { private: virtual void onTick() const; ... };
✦ Private inheritance means is-implemented-in-terms of. It’s usually inferior to composition, but it makes sense when a derived class needs access to protected base class members or needs to redefine inherited virtual functions. //访问保护成员或重定义虚函数
✦ Unlike composition, private inheritance can enable the empty base optimization. This can be important for library developers who strive to minimize object sizes. //空白基类最优化
采用组合方式更好:
class Widget { private: class WidgetTimer: public Timer { public: virtual void onTick() const; ... }; WidgetTimer timer; ... };
Item40:明智而审慎地使用多重继承
钻石型多重继承(deadly MI diamond)虚继承来达到子类只有一份基类数据目的(但代价不小)
多重继承使用场景:public继承某个Interface class,private继承某个协助实现的class。
模板与泛型编程
Item41:了解隐式接口和编译期多态
对模板而言,接口是隐式的,多态表现在template具象化和函数重载解析,发生在编译期。Item42:了解typename的双重意义
声明template参数时,前缀关键字class和typename可以互换使用typename标识嵌套从属类型名称(但最终解释权属于编译器)
typename C::const_iterator iter(container.begin());
Item43:学习处理模板化基类内的名称
由于存在模板特例化的可能,编译器并不能直接识别模板化基类内的名称:template<typename Company> class LoggingMsgSender: public MsgSender<Company> { public: void sendClearMsg(const MsgInfo& info) { sendClear(info); //sendClear是基类方法,但这样编译会报错 } ... };
可以采用:
this->sendClear(info); //方法一:使用this
using MsgSender::sendClear; //方法二:使用using
MsgSender::sendClear(info); //方法三:使用作用域运算符
Item44:将参数无关代码抽离template
非类型模板参数造成的代码膨胀:以函数参数或者成员变量替换template<typename T> class SquareMatrixBase { protected: ... void invert(std::size_t matrixSize); }; template<typename T, std::size_t n> class SquareMatrix: private SquareMatrixBase<T> { private: using SquareMatrixBase<T>::invert; public: void invert() { invert(n); } //以函数参数代替模板参数 };
Item45:运用成员函数模版接收所有兼容类型
模板类的继承实现——成员函数模板template<class T> class shared_ptr { public: shared_ptr(shared_ptr const& r); template<class Y> shared_ptr(shared_ptr<Y> const& r); //成员函数模板 shared_ptr& operator=(shared_ptr const& r); template<class Y> shared_ptr& operator=(shared_ptr<Y> const& r); //成员函数模板 };
成员函数模板是为了泛化,当“泛化拷贝构造函数”和“泛化的赋值构造”时,仍然需要声明正常的拷贝构造函数和赋值构造函数(不改变C++特性)
Item46:需要类型转换的时请为模版定义非成员函数
这是与普通类条款24对应的模板类条款。但不能在类外直接定义(类型推导不可行),其形式只能是友元的定义和声明在一起,如下:
template<typename T> class Rational { public: ... friend const Rational operator*(const Rational& lhs, const Rational& rhs) { return Rational(lhs.numerator() * rhs.numerator(), lhs.denominator() * rhs.denominator()); } };
Item47:请使用traits classes表现类型信息
特性萃取技术(对我而言是新知识,作者讲到的实现真是令人叹为观止)template < ... > class list { public: class iterator { public: typedef bidirectional_iterator_tag iterator_category; ... }; }; <typename IterT> struct iterator_traits { typedef typename IterT::iterator_category iterator_category; ... }; template<typename T> // partial template specialization struct iterator_traits<T*> // for built-in pointer types { typedef random_access_iterator_tag iterator_category; };
Item48:认识模版元编程
template<unsigned n> struct Factorial { enum { value = n * Factorial<n-1>::value }; }; template<> { enum{value=1}; };
杂项讨论
Item53:不要轻易忽略编译器的警告
略Item54:让自己熟悉包括TR1在内的标准程序库
tr1简直就是c++11的新特性!Item55:让自己熟悉Boost
略总结
终于看完了,杂项其实很重要,但的确需要大把时间去看,我也没罗列一些东西,暂且不提。第五章的实现内容,主要是从高效的角度阐述实现,譬如定义延后、内联、少用转换。其中异常安全值得细读。
第六章偏重于继承的讲解,其实暗带多态。继承和多态是一对难兄难弟,多态其实就是面向接口编程,继承一直为人诟病,组合也早成为其后继者。第35条取代虚函数的方法很好。
第七章看的最慢,也最难懂(我没写过类模板)。感觉还是偏重理论而非工程了,后半段倒是有点意思,譬如第47条。
相关文章推荐
- 《Effective C++》 读书笔记之三 资源管理
- 读书笔记 Effective C++
- effective C++ 10_令operator=返回一个reference to *this 读书笔记
- 读书笔记《Effective c++》 条款05 了解c++默默编写并调用哪些函数
- 读书笔记《Effective c++》 条款16 成对使用new和delete时要采用相同形式
- 读书笔记 Effective C++: 03 资源管理
- 《Effective C++》读书笔记:别让异常逃离析构函数
- 《Effective C++》读书笔记之item27:尽量少做转型动作
- effective C++ 读书笔记 条款20
- effective C++ 读书笔记 条款14 以对象管理资源
- Effective C++ 条款总结 读书笔记(二)
- Effective C++ 读书笔记(18)
- Effective C++ 读书笔记(八)
- Effective C++ 读书笔记(五) 实现
- 读书笔记《Effective C++》条款06:若不想使用编译器自动生成的函数,就该明确拒绝
- 《effective c++》读书笔记
- 读书笔记《Effective C++》条款33:避免遮掩继承而来的名称
- Effective C++ 读书笔记
- 《Effective C++》第4章 设计与声明(1)-读书笔记
- 【Effective C++ 读书笔记】导读 Introduction