Effective C++ 读书笔记三

  • 实现
    • 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(expression) 
dynamic_cast(expression) 
reinterpret_cast(expression) 
static_cast(expression)

特别是避免使用dynamic_cast,底层实现复杂,效率太慢。可采用下面方式:

typedef std::vector<std::tr1::shared_ptr > 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 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 bgImage; 
    int imageChanges;
};
class PrettyMenu 
{
     ...
private:
    Mutex mutex; 
    std::tr1::shared_ptr 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 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的策略模式

这就是一般书籍上介绍的策略模式。

Effective C++ 读书笔记三_第1张图片

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)
Effective C++ 读书笔记三_第2张图片

虚继承来达到子类只有一份基类数据目的(但代价不小)
Effective C++ 读书笔记三_第3张图片

多重继承使用场景:public继承某个Interface class,private继承某个协助实现的class。

模板与泛型编程

Item41:了解隐式接口和编译期多态

对模板而言,接口是隐式的,多态表现在template具象化和函数重载解析,发生在编译期。

Item42:了解typename的双重意义

声明template参数时,前缀关键字class和typename可以互换
使用typename标识嵌套从属类型名称(但最终解释权属于编译器)

typename C::const_iterator iter(container.begin());

Item43:学习处理模板化基类内的名称

由于存在模板特例化的可能,编译器并不能直接识别模板化基类内的名称:

template
class LoggingMsgSender: public MsgSender 
{ 
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 
{ 
private:
    using SquareMatrixBase::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 const& r); //成员函数模板
    shared_ptr& operator=(shared_ptr const& r);
    template<class Y>
    shared_ptr& operator=(shared_ptr const& r); //成员函数模板
};

成员函数模板是为了泛化,当“泛化拷贝构造函数”和“泛化的赋值构造”时,仍然需要声明正常的拷贝构造函数和赋值构造函数(不改变C++特性)

Item46:需要类型转换的时请为模版定义非成员函数

这是与普通类条款24对应的模板类条款。
但不能在类外直接定义(类型推导不可行),其形式只能是友元的定义和声明在一起,如下:

templateT> 
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;
        ... 
    };
};


struct iterator_traits {
    typedef typename IterT::iterator_category iterator_category;
    ... 
};

templateT> // partial template specialization struct iterator_traits<T*> // for built-in pointer types
{
    typedef random_access_iterator_tag iterator_category;
};

Item48:认识模版元编程

template
struct Factorial { 
    enum { value = n * Factorial1>::value };
};
template<>
{ 
    enum{value=1};
};

杂项讨论

Item53:不要轻易忽略编译器的警告

Item54:让自己熟悉包括TR1在内的标准程序库

tr1简直就是c++11的新特性!

Item55:让自己熟悉Boost

总结

终于看完了,杂项其实很重要,但的确需要大把时间去看,我也没罗列一些东西,暂且不提。
第五章的实现内容,主要是从高效的角度阐述实现,譬如定义延后、内联、少用转换。其中异常安全值得细读。
第六章偏重于继承的讲解,其实暗带多态。继承和多态是一对难兄难弟,多态其实就是面向接口编程,继承一直为人诟病,组合也早成为其后继者。第35条取代虚函数的方法很好。
第七章看的最慢,也最难懂(我没写过类模板)。感觉还是偏重理论而非工程了,后半段倒是有点意思,譬如第47条。


你可能感兴趣的:(Effective C++ 读书笔记三)