您的位置:首页 > 编程语言 > C语言/C++

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

总结

清明时节雨纷纷,转眼已经到清明,来杭许久,入职也近两周。上班加班太多,学习时断时续。其实,已经转战客户端开发的我,略显迷茫。这本书啊,明天看完吧!!!

实现

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条。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: