Effective C++读书笔记(十)继承与面向对象设计部分(上)

读书笔记中涉及到的所有代码实例可通过https://github.com/LuanZheng/EffectiveCPlusPlus.git进行下载获得。

Item32 确定你的public继承塑模出is-a关系

以C++进行面向对象编程,最重要的一个原则是:public inheritance意味“is-a”的关系。适用于base class身上的任何一件事情也适用于derived class.

Item33 避免遮盖继承而来的名称

C++的名称遮盖规则所做的唯一事情就是:遮盖名称。至于名称是否应和相同或不同的类型,并不重要。

派生类作用域被嵌套在基类作用域内。
local作用域>派生类作用域>基类作用域>namespace作用域>global作用域

如果继承的派生类的方法遮盖了基类的方法,而在派生类中又期望调用基类的被遮盖的方法,那么必须为那些原本会被遮盖的方法在派生类中添加using声明式。

class Derived : public Base
{
public:
       using Base::mf1;     //使用using之后,可以使得基类中被遮盖的函数在派生类中重见光明
       using Base::mf3;
       void mf1();         //纯虚函数在派生类中需要有具体实现
       void mf1(int i, int j); //派生类中的mf1遮盖了基类的所有mf1,不论参数是否匹配
       void mf3();         //派生类中的mf3遮盖了基类的所有mf3,不论参数是否匹配
       void mf5();
};

在使用private继承时,派生类想使用基类中的部分方法,可以利用转交函数来实现。

#ifndef _PRIVATE_DERIVED_H_
#define _PRIVATE_DERIVED_H_
#include "Base.h"
class PrivateDerived : private Base
{
public:
       //inline转交函数,在此处不用using,因为private继承,目的本在于不要暴露base class全部接口
       void mf1() { Base::mf1(); }
};
#endif // !_PRIVATE_DERIVED_H_

例子见Item33

Item34 区分接口继承和实现继承

pure virtual函数有两个最突出的特性:它们必须被任何“继承了它们”的具象class重新声明,而且它们在抽象class中通常没有定义。

声明一个pure virtual函数的目的是为了让派生类只继承函数接口。
声明简朴的(非纯)虚函数的目的,是让派生类继承该函数的接口和缺省实现。
声明non-virtual函数的目的是为了让派生类继承该函数的接口及一份强制性实现。

当派生类增加种类时,可能导致原来的行为发生变化。比如,原来AirplaneModelA和AirplaneModelB均继承自Airplane。而Airplane中的方法fly()适合ModelA&ModelB。但若引进一种新的AirplaneModelC,且AirplaneModelC的飞行方式与A,B不同,但使用者可能会犯错,仍旧使用基类的fly方法作用在ModelC上。在这种情况下,将基类的fly方法改为纯虚函数,就保证了每个派生类都需要重新定义和实现他,因此,也就强制避免前面错误发生的可能性。若ModelA&B仍想共用原来的方法,避免代码的重复,可以在基类中重新定义一个新的fly方法,defaultfly,而另ModelA&B的fly方法简单调用基类的defaultfly方法即可。

另一种方法是,借用“pure virtual函数必须在派生类中重新声明,但它们也可以拥有自己的实现”这一事实,让派生类的fly方法调用基类的同名fly方法(虽然是pure virtual)。

例子见Item34

Item35 考虑virtual函数以外的其他选择

籍由Non-Virtual Interface手法实现Template Method模式

令“客户通过public non-virtual成员函数间接调用private virtual函数”,称为NVI手法。

NVI手法涉及在派生类内重新定义private virtual函数。但这里并不存在矛盾。“重新定义virtual函数”表示某些事“如何”被完成,“调用virtual函数”则表示它“何时”被完成。NVI手法允许派生类重新定义虚函数,从而赋予它们“如何实现机能”的控制能力,但基类保留诉说“函数何时被调用”的权利。

籍由Function Pointers手法实现Strategy模式

将函数指针作为参数传入到对象中,令类的某个成员函数来调用传入的函数,则可增加弹性(具体执行哪个函数由参数来决定,函数参数可运行期动态修改)。

   typedef int(*HealthCalcFunc)(const GameCharacterFP&);      //Function points 

   //弹性:同一类型可以有不用的健康计算函数
   //弹性:健康计算函数可在运行期动态变更
   GameCharacterFP* gcFPQ = new GameCharacterFPA(loseHealthQuickly);  
   GameCharacterFP* gcFPS = new GameCharacterFPA(loseHealthSlowly);

籍由tr1::function手法实现Strategy模式

利用tr1::function手法,可以获得更大的灵活性。比如前面使用函数指针方法,如果loseHealthQuickly返回值是short,而不是int,函数指针无法进行转换。而使用tr1::function就可以转换。且tr1::fucntion还可以支持成员函数,函数对象等方式。

1>e:\gitrepository\effectivecplusplus\effectivecplusplus\item35\main.cpp(24): error C2664: “GameCharacterFPA::GameCharacterFPA(GameCharacterFPA &&)”: 无法将参数 1 从“short (__cdecl *)(const GameCharacterFP &)”转换为“GameCharacterFP::HealthCalcFunc”
       //TR1::function
       GameCharacterATR1Func* gcATR1F = new GameCharacterATR1Func(loseHealthQuicklyTR1);  //与FP方法使用起来相同,short会自动转int
       gcATR1F->healthValue();

       //利用成员函数来计算的方法
       GameLevel currentLevel;
       GameCharacterATR1Func* gcATR1FMemberCalc =
              new GameCharacterATR1Func(std::tr1::bind(&GameLevel::health, currentLevel, std::tr1::placeholders::_1));
       gcATR1FMemberCalc->healthValue();
       //利用函数对象来实现
       std::cout << std::endl;
       GameCharacterATR1Func* gcATR1FuncObj =
              new GameCharacterATR1Func((*gcATR1FMemberCalc)());
       (*gcATR1FuncObj)();

补充函数对象,该部分引用自(https://www.cnblogs.com/ljygoodgoodstudydaydayup/p/5813247.html
既然函数对象与函数指针在使用方式上没什么区别,那为什么要用函数对象呢?很简单,函数对象可以携带附加数据,而指针就不行了。下面例子中,n即为附加数据。

class Less
{
public:
    Less(int num) :n(num) {}
    bool operator()(int value)
    {
        return value < n;        //可以使用n,n为附加数据。
    }
private:
    int n;            //这里的n即为前面提到的附加数据
};
int main()
{
    Less isLess(10);
    cout << isLess(9) << " " << isLess(12); // 输出 true         false

    system("pause");
    return 0;
}

例子见Item35

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值