effective c++学习笔记(条款32-条款40)

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

Public 继承意味着”is-a”(是一种)的关系。也就是说使用public继承,每一个Derived类的对象同时也是一个Base class对象,适用于base class身上的没一件事一定也适用于Derived calss。根据这一观点,Penguin(企鹅)虽然也属于鸟,但不能继承Bird,因为Penguin不能象Bird一样飞,Square(正方形)也不能继承Rectangle(矩形),因为Square不象Rectangle长宽独立。

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

1.      Derived类内成员函数内变量名字查找规则:先在Derived类成员函数local作用域中查找变量名,如果没有,在Derived作用域中查找,如果没有找到,在Base class作用域中查找,如果没有找到,在Base namespace所在的作用域中查找,如果没有找到,在global作用域中查找。即较小作用域的名字会遮掩较大作用域的名字。注意:这里说的是名称,与名称的类型无关

2.      由1知,Derived class内的名字会遮掩Base内的名字。如果不清楚这些,可能导致某些错误。比如:

Class Base{

Pirvate:

Int x;

Public:

    virtualvoid mf1(int);

   void mf3(double);

    …

};

class Derived:public Base{

public:

virtual voidmf1();

void mf3();

}

Derived d;

d.mf1(x);  //错误,因为Derived::mf1遮掩了Base::mf1

d.mf3(x); //错误,因为Derived::mf3遮掩了Base::mf3

3.由2知,Derived不能从Base中继承重载函数。但这违背了public继承,base和derived之间的is-a关系。解决方案:使用using声明式或转交函数。

(1)using声明式:

class Derived:public Base{

public:

usingBase::mf1;

usingBase::mf3;

virtual vod mf1();

void mf3()

}

(2)using声明式有它的缺点:它会使某名称的所有同名函数在derived class中都可见。可以使用转交函数。

Class Derived:private Base{

public:

virtual voidmf1()

{Base::mf1();}

…..

}

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

1.      声明pure virtual函数的目的:Base类让Derived class只继承函数接口,但Base class无法为接口提供合理的缺省实现。也就是说Base要求Derived必须实现一个同类型的函数,但怎样实现不要求。对于pure virtual函数,Derived必须实现。

2.      声明impure virtual函数的目的:Derived继承Base的函数接口和缺省实现。也就是说Base要求Derived支持一个同类型的函数,如果Derived的实现与Base不同,可以重写它,如果不想实现,就使用Base提供的默认版本。对于impure virtual函数,Derived可以不实现。

3.      声明non-virtual函数的目的:Derived class继承函数的接口和一份强制性实现。也就是Derived class不应该重新定义Base class non-virtual函数的实现。

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

继承+public virtual是面向对象常用的设计方式。但并不代表只有这一种设计方式,可以考虑其他设计方式:

1.      Non-virtual interface(NVI):令base的publicnon-virtual作为函数接口,然后接口调用一个private virtual函数来实现继承类的差异化。该方法的一个优点:可以在virtual函数的前面和后面做一些”事前工作”和”事后工作”。

2.      将virtual函数替换为“函数指针成员”,这是Strategy(策略)模式的一种表现形式。通过使用不同的策略来表现差异化。

3.      但使用函数指针,必须要求策略类型必须和定义的函数指针类型完全相同。如果策略声明为函数对象、成员函数、返回值和参数不完全相同,但可以引式转换,怎么办。解决方案:用tr1::funciton替换函数指针,它可接纳“与给定的目标签名式兼容”的所有可调用物。

4.      使用传统的Strtegy模式。即将差异的部分仍然是由外部实现的不同策略实现,但策略的实现不再使用函数指针,而是用另一个类的virtual函数实现。即将继承体系内的virtual函数替换为另一个继承体系内的virtual函数。

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

原因:(1)如果重新定义继承而来的non-virtual函数,则用基类指针指向继承类对象,调用的是基类对象的函数,用继承类指针指向继承类对象,调用的是继承类对象的函数。这样就一个对象的函数即可能出现有时表现为继承类行为,有时表现为基类形式不一致的情况。

(2)对于public继承,如果重新定义继承的non-virtual函数,违背Base与Derived的 is-a关系

(3)根据条款34,non-virtual函数要求继承类必须继承函数接口和实现,不允许修改。

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

原因:virtual函数由动态绑定,缺省参数值却是静态绑定。例子:

class Shape{

public:

  enum ShapeColor{Red,Green,Blue};

  virtual void draw(Shape color = Red)const = 0;

   ….

}

class Rectange:public Shape{

public:

virtual voiddraw(ShapeColor color = Green)const;

};

Shape* pr = new Rectangle;

Pr->draw(); //这里调用Rectangel的draw,但是却是使用Shape的默认函数参数。

解决方案之一:使用条款35的NVI方式:另base类的一个public non-virtual作为函数接口,接口提供默认参数值,然后该函数调用一个private virtual函数,该函数可被derived重新定义。由于non-virtual函数应该绝对不被base重写,所以可以使参数的参数保持不变。

条款38:通过复合塑模出has-a或“根据某物实现出”

1.      复合(聚合):当一个类对象包含其他类型的对象,便是复合关系。

2.      复合有两种意义:has-a或is-implemented-in-terms-of(根据某物实现出)。当复合发生于应用域内的对象之间,表现出has-a的关系;当发生于实现域内则表现is-implemented-in-terms-of的关系。

3.      应用域:程序描述世界中的某些事物,如人、汽车、视频画面等;应用域:用于实现应用域的对象,如缓冲区、互斥器、查找树等。

4.      复合的意义和public继承不用,复合关系不能用public继承实现。如模板库的set用平衡查找树实现,查找速度快,但浪费空间,对于空间比速度重要的场合不适合。一个解决办法是复用list来实现一个set,保证插入数据的唯一。这时不能用public继承,因为它们不是is-a的关系。正确的方法是:使用复合,set对象可根据一个list实现出来,它们是is-implemented-in-terms-of关系。

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

1.      Private继承意味Derived和Base是implement-in-terms-of(根据某物实现出)的关系,在软件“设计”层面上没有意义,其意义只在软件实现层面。

2.      复合也可以表现implement-in-terms-of的关系。尽可能使用复合,必要时才使用private继承。

3.      使用private继承必要的情况:当derived class需要访问protected base class的成员或需要重新定义继承而来的virtual函数;另一个的情况:base 没有non-static成员变量,没有vitual函数,并且对“对象尺寸最小化”要求较高。原因:private继承可以造成empty base最优化。

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

1.      多重继承比单一继承复杂,它可能造成某些歧义性。比如两个base与相同的函数名,当derived调用该函数时,造成访问不明确。

2.      当两个base(base1、 base0)又共同继承与另一个base(base0),则继承与两个base的Derived就有两份base0的数据,对base0的数据的访问也会造成歧义。

3.      对于问题2的解决:让base0成为virtual base class。具体做法是:base1 base2继承base0时采用virtual 继承。例子:

class base1:virtual public base0{ … };

class base2:virtual public base0{ …};

但使用virtual继承的类所产生的对象往往比用non-virtual继承的对象体积大,访问virtual base class的成员变量时,也比访问non-virtual base class的成员变量慢。所以非必要不要使用virtual base。如果必要使用,尽可能避免在其中存储变量。就象java和.net的interface一样。

4.      使用多重继承的一个合理场合:Derived需要pulib继承某个接口,并且private继承某个协助实现的类。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值