您的位置:首页 > 编程语言 > C语言/C++

effective C++ 条款32 to 条款40

2010-11-22 11:03 369 查看
这一章是面向对象的概念

 

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

其实这个条款正如大师一开始举得例子一样:public inheritance 意味 is-a的关系

 

条款33:名称的遮掩

局部变量名汇遮掩全局作用域的变量名

 

看下面一个例子:

class Base{

private :

     int x ;

public :

    virtual void mf1 = 0 ;

    virtual void mf2() ;

    void mf3() ;

    ....

}

 

class Derived : public Base{

public :

    virtual void mf1() ;

    void mf4();

    ...

}

 

void Derived::mf4()

{

    ....

    mf2() ;

    ....

}

我们看mf4中mf2的查找顺序:先在mf4的作用域中找,没找到mf2,往外走,在Derived作用域找,没找到,往外,在base class中找到了,OK,结束,如果还是没找到的话,就再namespace作用域中找,最后到global作用域找

 

上面一切OK,但是一旦基类有重载成员,而继承类只改写了一个,那么,基类的其他重载函数就不可见了,看下面的例子:

class Base{

private :

     int x ;

public :

    virtual void mf1 = 0 ;

    virtual void mf1(int) ;

    virtual void mf2() ;

    void mf3() ;

    void mf3(double) ;

    ....

}

 

class Derived : public Base{

public :

    virtual void mf1() ;

    void mf3() ;

    void mf4();

    ...

}

 

Derived d ;

int x ;

d.mf1() ; 

d.mf1(x) ; // error

d.mf2() ;

d.mf3() ;

d.mf3(double) ; // error

好,如何解决上面的问题,两种方案,一是用using ,而是用转接函数

方案1:

class Derived : public Base{

public :

    using Base::mf1 ;

    using Base::mf3 ;

    virtual void mf1() ;

    void mf3() ;

    void mf4();

    ...

}

这样一改上面就不会错了

 

方案2:可能using编译器不支持,也可能你不想全部要,只要一部分

class Derived : private Base{

public :

    virtual void mf1()

    {

        Base::mf1() ;

    }

}

 

总结:

1、derived classes内的名称会遮掩base classes内的名称,在public 继承下,从来没有人希望如此

2、为了让被遮掩的base名称能够重见天日,可以用两种方案:使用using或者使用转交函数

 

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

核心概念:区分三种函数限制符的意义:

1、纯虚函数(pure virtual):

     只继承接口,可以给纯虚函数提供定义,但调用的唯一途径是调用时明确指出其class名称

2、非纯虚函数(impure virtual):

    继承该函数的接口和缺省实现(书上的那个飞机的例子有点儿小夸张了)

3、非虚函数(non-virtual):

    继承接口及一份强制性实现

 

条款35:看不懂

 

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

一个例子解决问题:

class B{

public : void mf() ;

}

 

class D : public B {

}

 

D x ;

B * pB = &x ;

D* pD = &X  ;

pB->mf() ; 调用B的mf

pD->mf() ; 调用D的mf

 

 

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

首先,你只能继承两种函数:nont-virual和virtual,如上面一个条款说,不能重新定义non-virutal函数,因此,我们可以说:绝不重新定义继承而来的virtual函数的缺省参数值。

关键概念:多态是动态绑定的,而缺省参数是静态绑定的。所以,如果你重定义继承而来的virtual函数的缺省参数值,那么,你是用基类的缺省参数值来调用继承类的函数。

class Shape{

public :

    enum ShapeColor{Red , Green , Blue} ;

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

    ...

}

 

class Rectangle : public Shape{

public :

     virtual draw(ShapeColor color=Green ) const ;

}

 

class Circle : public Shape{

public :

     virtual draw(ShapeColor color) const ;

}

我们看到问题了吧,是的,你可能会再“调用一个定义于derived class内的virtual函数”的同时,却是用base class为它所指的缺省参数。

 

解决方案:

1、如果要重新定义,用一样的参数,但是,那样是代码重复,我们受不了,而且改动一处,忘了改另外一处就不好了

2、看下面的设计:

class Shape{

public :

    enum ShapeColor(Red , Greed , Blue) ;

    void draw(ShapeColor color= Red) const    // 继承类不要重写该方法。该方法提供默认实参,并且调用真正办事的private函数

    {

         doDraw(color) ;

     }

     ...

private :

     virtual void doDraw(ShapeColor color) const = 0 ; //真正的工作,基类类需要重写

}

 

class Rectangle : public Shape{

public :

     ...

private :

    virtual void doDraw(ShapeColor color) cosnt ;

}

 

牢记的概念:绝不重新定义一个继承而来的缺省参数值,因为缺省参数是静态绑定的,而virtual函数----你唯一应该覆写的东西,是动态绑定的

 

条款38:复合表示has-a 或者 “根据某物实现”

举了两个例子:

1、人有地址啊,电话号码啊,这个叫has-a

2、可以用一个list来实现一个set,这个叫根据某物实现

其实,没什么,我觉得2就像是“适配器模式”

 

条款39:private继承

先讲两个概念

1、编译器不会自动将一个derived class对象转换为一个base class 对象。

2、由private base class继承而来的所有成员,再derived class中都会变成private属性,纵使他们在base中是public活着protected的

 

private继承表示根据某物实现。private继承意味着实现部分被继承,接口部分应略去(因为private继承完后都是private的,外界根本看不到)。如果D以private继承B,意思是D对象根据B对象实现而得,再没有其他含义了。

private继承意味is-implemented-in-terms-of(根据某物实现),这个和条款38说的复合一致。大师指出:尽量用复合,只有当protected成员和/或virtual函数牵扯进来的时候,还有就是空间方面的利害关系。下面,我们分两个例子来看:

 

class Timer{

public :

    explicit Timer(int tickFrequency) ;

    virtual void onTick() const ;

}

 

class Widget : private Timer{

private : // private继承的东西都是private的了,别乱写,搞死别人

    virtual void onTick() ;

}

 

大师说了,其实在这种情况下复合还是可以代替private继承,并且,他说了两个优点,我们看看代码吧,优点小夸张,呵呵

class Widget{

private :

    class WidgetTimer : public Timer{

    public:

         virtual void onTick() const ;
    };

    WdigetTimer timer ;

};

 

空间方面的就是说继承比复合省空间,有一个EBO(empty base optimization 空白基类最优化)

 

我的总结:能不用private继承就不用private继承,我在实际的工作中还想从来没有用过,哈哈

 

条款40:多重继承

考虑下面的情况:

class A {

}

class B : public A {

}

class C : public A {

}

class D : public B , public D{

}

 

第一个问题:

加入B,C中都有一个mf()函数,那么

D d ;

d.mf() ; //调用的是哪个?歧义

应该这么用:d.B::mf() ;  // B的mf

 

第二个问题:

A中的一份数据data,B也有了,C也有了,那么D中有几份?

缺省做法是2份,但可以通过virtual继承防止上面的事情发生

class B :  virtual public A {

}

class C : virtual public A {
}

那么是不是所有的继承都应该用virtual public呢?

不是,如果你用virtual public , 你必然要是靠编译器在背后默默支撑你,因此,代码的体积将增大,速度将变慢。并且,virtual base class的初始化规则也复杂的多。

大师给了两个观点:

1、非必要不要用virtual base

2、如果非要用,尽量不要在里面放置数据,这样不用担心初始化的事情。(和JAVA 的 interface差不多)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息