EffectiveC++学习笔记-条款34|35

来源:互联网 发布:no sleep for mac 编辑:程序博客网 时间:2024/04/29 16:36

条款34 区分接口继承和实现继承
条款35 考虑virtual函数以外的选择

区分接口继承和实现继承

在设计类成员函数时,一般既不要将所有函数都声明为non-virtual(普通函数),这会使得没有余裕空间进行特化工作;也一般不要将所有函数都声明为virtual(虚函数或纯虚函数),因为一般会有一些成员函数是基类就可以决定下来的,而被所有派生类所共用的。这个设计法则并不绝对,要视实际情况来定。

总结

  1. 接口继承和实现继承不同。在public继承之下,derived class总是继承base class的接口;
  2. pure virtual函数只具体指定接口继承;
  3. impure virtual函数具体指定接口继承和缺省实现继承;
  4. non-virutal函数具体指定接口继承以及强制性实现继承。

考虑virtual函数以外的选择

使用non-virtual interface的方式实现Template Method模式

直接上代码:

class Base{public:    virtual void work() const  //work    {        cout <<"Base::work"<<endl;        dowork();    }private:    //具体的work  子类需要实现自己的dowork() 以便 work()函数调用    //子类不实现默认调用 Base的dowork()    virtual int dowork() const    {        cout <<"Base do work"<<endl;    }};class Derived: public Base{public:private:    virtual int dowork() const    {        cout <<"Derived do work "<<endl;    }};int main(int argc, char *argv[]){    Derived d;    d.work();    return 0;}//打印结果Base::workDerived do work

显然不重写dowork函数是调用父类的函数。这种方法和“钩子方法“使用的原理是差不多的。
这个函数也被称为virtual函数的外覆器(wrapper)。

使用function pointers的方式实现Strategy模式

例如在设计一个游戏角色的健康成长设置的时候。我们可以直接指向一个健康计算函数,可直接调用函数进行计算

class GameCharacter;//缺省的健康计算 (正常人)  还可能有体力好的人 恢复健康就很快等等int defaultHealthCalc(GameCharacter&);class GameCharacter{public:    typedef int (*healthCalcFunc)(GameCharacter& gc);    explicit GameCharacter(healthCalcFunc hcf = defaultHealthCalc) : m_hcf(hcf){}    //获取一个角色的健康值    int healthValue() const    {        return m_hcf(*this);    }private:    healthCalcFunc m_hcf;};

这样使用函数指针的方式,弹性就更大了。
我们的子类可以继承这个类:

class Guy : public GameCharacter{public:    explicit Guy(healthCalcFunc hcf = defaultHealthCalc):GameCharacter(hcf){}    ...};//提供不同体质的人的计算方法int loseHealthQuickly(const GameCharacter&);int loseHealthSlowly(const GameCharacter&);//可以创建不同体质的人Guy g1(loseHealthQuickly);Guy g2(loseHealthSlowly);

当然可以提供函数设置这个函数指针。

但是这样还是有问题,当我们需要使用non-member函数访问class的non-public成份的唯一方法就是弱化封装。

使用tr1::function的方式实现Strategy模式

使用函数指针的约束很明显,必须是函数,固定的返回值与参数。但是tr1::function则没有这些现实,这样的对象可持有任何可调用物(函数指针、函数对象、成员函数指针),只要声明格式兼容与需求。
只需要将上面类的函数指针声明替换成:

typedef std::tr1:;function<int(const GameCharacter&)>  HealthCalcFunc;

举个简单例子:

void func(string str)//带调用的函数{    qDebug() <<"func:"<<str;}struct Func//函数对象{    void operator ()(string str)    {        qDebug() <<"Func:operator:"<<str;    }};std::function<void(string str)> pfunc;//声明int main(int argc, char *argv[]){    pfunc = func;    pfunc("hello");    Func f;    pfunc = f;    pfunc("world");    return 0;}//打印结果func:helloFunc:operator:world

常见的Strategy模式

这个想必都很熟悉了,实现比较简单,但是呢始终不如tr1::funtion和tr1::bind酷。。。

原创粉丝点击