Effective C++ 读书笔记(3)

本文探讨了C++设计中的关键原则与模式,包括正确使用继承、接口与实现的区别、多重继承的注意事项、模板编程技巧以及如何高效地进行类型转换等。文章提供了实用的指导方针,帮助开发者编写更高质量的C++代码。

条款32:确定你的public继承塑模出”is-a“关系

条款33:避免遮掩继承而来的名称

derived class内的名称会遮掩base class内的名称

条款34:区分接口继承和实现继承

pure virtual函数只具体指定接口继承
impure virtual函数具体指定接口继承及缺省实现继承
non-virtual函数具体指定接口继承及强制性实现继承

PS:可以为pure virtual函数提供定义,编译器不会报错

条款35:考虑virtual函数以外的其他选择

1.Non-Virtual Interface方法(NVI)

virtual函数为private,healthValue函数为public成员函数,调用private virtual函数,实际工作是在private virtual函数中完成的。这个non-virtual函数为virtual函数的外覆器

class Character
{
public:
    int healthValue() const
    {
    ...
    int retVal = doHealthValue();
    ...
    return retVal;
    }
private:
    virtual int doHealthValue() const
    {...}
};

2.运用Function Pointers

class Character; //前置声明
int defaultHealthCalc(const GameCharacter& gc);
class Character
{
public:
    typedef int (*HealthCalcFunc)(const Character&);
    explicit Character(HealthCalcFunc hcf = defaultHealthCalc)
        :healthFunc(hcf)
    {}
    int healthValue() const
    {return healthFunc(*this);}
private:
    HealthCalcFunc healthFunc;
};

优点

(1)同一人物类型之不同实体可以有不同的健康计算函数


(2)某已知人物之健康指数计算函数可在运行期变更

例如Character可提供一个成员函数setHealthCalculator,用来替换当前的生命计算函数

缺点

non-member函数无法访问non-public变量,可能需要降低class的封装性

3.利用tr1::function完成策略模式

class Character; //前置声明
int defaultHealthCalc(const GameCharacter& gc);
class Character
{
public:
    //HealthCalcFunc可以是任何“可调用物”,
    //可被调用并接受任何兼容于Character之物,返回任何兼容于int的东西
    typedef std::tr1::function<int (const Character&)> HealthCalcFunc;
    explicit Character(HealthCalcFunc hcf = defaultHealthCalc)
        :healthFunc(hcf)
    {}
    int healthValue() const
    {return healthFunc(*this);}
private:
    HealthCalcFunc healthFunc;
};

条款36:绝不重新定义继承而来的non-virtual函数

任何情况下都不应该重新定义一个继承而来的non-virtual函数

条款37:绝不重新定义继承而来的缺省参数值

原因:virtual函数为动态绑定,但缺省参数值却是静态绑定

动态类型与静态类型的区别


Shape *ps;
Shape* pc = new Circle;
Shape* pr = new Rectangle;

ps,pc,pr都被声明为pointer-to-Shape类型,无论他们指向什么,静态类型都为Shape*

动态类型是“面前所指对象的类型”,即动态类型可以表现出一个对象将会有什么行为。因此pc动态类型为Circle*,ps没有动态类型,因为它没有指向任何类型

条款38:通过复合塑模出has-a或“根据某物实现出(is-implemented-in-terms-of)”

当复合发生在应用域内的对象之间,表现出has-a关系

当复合发生在实现域内的对象之间,表现出is-implemented-in-terms-of关系

条款39:明智而审慎的使用private继承

private继承意味着is-implemented-in-terms-of。如果你让class D以private方式继承class B,用意是为了采用class B内已经备妥的某些特性,而不是因为B和D存在任何观念上的关系。

private继承意味着只有实现部分被继承,接口部分略去。

更多推荐使用复合,而不是private继承

条款40:明智而审慎的使用多重继承

多重继承可能会出现“钻石型多重继承”,解决方法是利用virtual base class

使用虚继承缺点:

(1)体积大

(2)速度慢

(3)增加初始化成本,即class若派生自virtual base而需要初始化,必须追溯其virtual base--无论那些base距离多远

因此建议少用虚继承,如果使用,最好virtual base class不带数据

条款41:了解隐式接口和编译期多态

编译期多态:以不同的template参数具现化function templates 会导致调用不同的函数

显式接口:在class内,由函数的签名式(即函数名称、函数类型、返回类型)构成

隐式接口:由有效表达式组成

条款42:了解typename的双重意义

在使用模版时,class和typename大多情况下是没有区别的,但有时必须得使用typename

例:

template<typename C>
void funC(const C &c)
{
    if(c.size()>2)
    {
        C::const_iterator iter(c.begin());
        ++iter;
    }
}

template内出现的名称如果依赖于某个template参数,被称为从属名称,如例中的C::const_iterator

若从属名称在class内嵌套,则被称为嵌套从属名称,如C::const_iterator

不依赖于任何template参数的名称,叫做非从属名称

template<typename C>
void funC(const C &c)
{
    C::const_iteraot * x;
}

上面代码中,如果C::const_iterator不是一个类型,而是C中的一个static成员变量const_iterator,或x是一个global变量,那么C::const_iteraot * x中的*号就是乘号的意义了。

在C++规则中,若解析器在template中遇到一个嵌套从属名称,便假设这名称不是一个类型,除非我们告诉它是。

解决此问题的方法是在前面加上typename

template<typename C>
void funC(const C &c)
{
    typename C::const_iteraot * x;
}

例外情况:

typename不可以出现在base class List内的嵌套从属类型名称之前,也不可在member initialization list(成员初值列)中作为base class修饰符

template<typename T>
class D:public Base<T>::B //base class list中不允许typename
{
public:
    D(int x)
        :Base<T>::B //初值列中中不允许typename
    {}

条款43:学习处理模板化基类内的名称

条款44:将与参数无关的代码抽离templates

条款45:运用成员函数模版接受所有兼容类型

使用成员函数模版来处理可接受所有兼容类型的函数,例如某class的构造函数,我们想要写出可以兼容所有类的构造函数,如A(int x),A(double x),A(vector<int> x)等,那么这时用到的就是泛型编程写出一个成员函数template

template<typename T>
class A
{
public:
    template<typename U>
    A(const A<U> &other);  //成员函数模版,为了生成copy构造函数
};

PS:在class内声明泛化的copy构造函数(member template),并不会阻止编译器生成它们自己的copy构造函数,相同规则也适用于赋值操作符

条款46:需要类型转换时请为模版定义非成员函数

条款47:请使用traits class表现类型信息

条款48:认识template元编程

模版元编程可将工作由运行期移往编译期,因为得以实现早期错误侦测和更高的执行效率,但是编译期时间会增长

条款49:了解new-handler的行为

条款50:了解new和delete的合理替换机制

条款51:编写new和delete时需固守常规

条款52:写了placement new 也要写placement delete

条款53:不要轻忽编译器的警告

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

条款55:让自己熟悉Boost


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值