Effective C++第六章-继承与面向对象设计

来源:互联网 发布:超级英雄官方数据 编辑:程序博客网 时间:2024/05/18 06:48

几种继承方法

  • 类型之间的public继承–is a(derived class is a base class.)

  • 类型之间的复合关系–has a或根据某物实现出

    class address{...};class phonenumber{...};class person{public:...private:std::string name;address theaddress;phonenumber thephone;};
  • private继承:意味着根据某物实现出(如果D以private形式继承B,意思是D对象根据B对象实现而得)

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

    通常比复合的级别低。但是当你面对“并不存在is-a关系”的两个class,其中一个需要访问另一个的protected成员,或需要重新定义其一或多个virtual函数,设计为private继承是合理的。

  • 多重继承(multiple inheritance,MI)-继承一个以上的基类

    //多重继承可能带来的歧义问题class first{public:void checkout();};class sec{private:virtual void checkout();};class thir:public first,public sec{...};thir mp;mp.checkout();//wrong,first和sec都有checkout函数,且只有first的public成员函数checkout是可取用的。但是C++是先找出最佳匹配函数后才检验可取性,first和sec的checkout具有相同的匹配程度,因此造成歧义mp.first::checkout();//rightmp.sec::checkout();//wrong,提示“尝试调用private成员函数”的错误。

避免继承中的名称遮掩

int x;void func(){    double x;    std::cin>>x;}//局部的double x遮掩全局的int x

在类的继承中,derived class作用域被嵌套在base class作用域。

class base{public:    int x;private:    virtual void mf1() = 0;//纯虚函数    virtual void mf2();    void mf3();};class derived:public base{public:    virtual void mf1();    void mf4();};

这里写图片描述

class base{public:    int x;private:    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();};

2

基于作用域的名称遮掩规则会使得base class内所有名为mf1和mf3的函数都被derived class内的mf1和mf3函数遮掩掉了。

derived d;int x;double y;d.mf1();//rightd.mf1(x);//wrongd.mf2();//rightd.mf3();//rightd.mf3(y);//wrong//不论是virtual还是普通函数,只要子类中有同名函数,不管形参是什么,编译器都不会再往父类作用域里面找了。

消除遮掩的方法:

  1. 利用using声明式

    如果你继承base class并加上重载,而你又希望重新定义或覆写其中一部分,那么你必须为那些原本会被遮掩的每个名称引入一个using声明式,否则某些你希望继承的名称会被遮掩。

    class base{public:int x;private:virtual void mf1() = 0;//纯虚函数virtual void mf1(int);virtual void mf2();void mf3();void mf3(double);};class derived:public base{public:using base::mf1;using base::mf3;//使得base class内名为mf1和mf3的所有在derived类作用域内都可见virtual void mf1();void mf3();void mf4();};
    derived d;int x;double y;d.mf1();//right,调用derived::mf1()d.mf1(x);//right,调用base::mf1(int)d.mf2();//right//调用base::mf2()d.mf3();//right//调用derived::mf3()d.mf3(y);//right,调用base::mf3(double)

    但using声明式会令继承而来的某给定名称之所有同名函数在derived class中都可见~适合于public继承(派生类继承基类的所有)

    using声明和using指示

  2. 利用转交函数(forwarding function)

    在private继承时可以实现:派生类只继承基类的部分,比如derived唯一想继承mf1的无参数版本。此时不能使用using声明式,使用转交函数:

    class base{...};//同前class derived:private base{publicvirtual void mf1()//转交函数{    base::mf1();}//暗自成为inline};
    derived d;int x;d.mf1();//rightd.mf1(x);//wrong

区分函数的接口继承和函数的实现继承

  • 声明一个纯虚函数的目的是为了让derived class只继承函数接口
  • 声明非纯虚函数(impure virtual)的目的是让derived class继承该函数的接口和缺省实现
  • 声明一个non-virtual函数的目的是为了令derived class继承函数的接口及一份强制性实现

成员函数的接口总是会被继承,下面分别通过代码详细说明。

  • 声明一个纯虚函数的目的是为了让derived class只继承函数接口。但是注意:

    1. 纯虚函数必须在派生类中重新声明

    2. 也可以为纯虚函数提供定义,例1

      --example1class airplane{public:virtual void fly()=0;};void airplane::fly(){//pure virtual函数的实现...}
  • 声明非纯虚函数(impure virtual)的目的是让derived class继承该函数的接口和缺省实现。如果derived class对该函数不想做出特殊行为,则可以用base class提供的缺省实现。

    class airplane{public:virtual void fly();};void airplane::fly(){//缺省实现}class modelA:public airplane{};//如果modelA不想使用缺省实现但是忘记自己对fly函数进行重新定义了呢?

    切断接口和缺省实现之间的连接:

    class airplane{public:virtual void fly()=0;protected://protected成员可以被派生类对象访问,不能被用户代码(类外)访问。void defaultfly();//该函数应该是non-virtual函数,因为任何derived class都不该重新定义该函数。};void airplane::defaultfly(){...}

    此时airplane类的接口为fly,缺省实现为defaultfly,若派生类想使用缺省实现可在fly函数中对defaultfly函数做一个inline调用。

    class modelA:public airplane{public:virtual void fly(){    defaultfly();}};class modelB:public airplane{public:virtual void fly();};void modelB::fly(){    ...}
  • 声明一个non-virtual函数的目的是为了令derived class继承函数的接口及一份强制性实现

    派生类绝不能重新定义non-virtual函数。

pure virtual函数、impure virtual函数、non-virtual函数之间的差异使你能精确指定想让derived class继承的东西:只继承接口、或是继承接口和一份缺省实现、或是继承接口和一份强制实现。

virtual函数的替换方案(涉及到了Template Method设计模式和Strategy设计模式)

有很多种替换方案~只提一下non-virtual interface(NVI),Template Method设计模式的一种特殊形式。以public non-virtual成员函数(该函数用来控制virtual函数被调用的方法和时间)包裹较低访问性(private或protected)的virtual函数(该函数用来具体实现)。

class base{private:    int Base_x;public:    int process() const    {        int Result = step1();        ...    }private:     virtual int step1() const     {         return Base_x;     }};class derived : public base{private:     virtual int step1() const//派生类可以重新定义     {        return Base_x*2;     }};

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

因为virtual函数是动态绑定的,但是缺省参数值是静态绑定的。

也就是说,有可能调用一个定义于derived class内的virtual函数的同时,却使用base class为它指定的缺省参数值。例:

class shape{public:    enum shapecolor{red, green, blue};    virtual void draw(shapecolor color = red) const = 0;//pure virtual,缺省参数值color};class rectangle:public shape{public:    virtual void draw(shapecolor color = green) const;//此处改变了缺省参数值};...//调用:shape* pc = new rectangle;//动态类型是rectangle,静态类型是shapepc.draw();//调用rectangle的draw,调用shape的color参数值

因此,需要注意:1、派生类改变(重新定义)缺省参数值。2、基类的缺省参数值的改变,则需要改变派生类。

可以采用其他设计替代virtual函数,例如NVI(non-virtual interface)

class shape {public:    enum shapecolor{red, green ,blue};    void draw(shapecolor color = red) const//draw函数是non-virtual函数,不能被覆写,因此color的缺省值不会被改变    {        dodraw(color);    }private:    virtual void dodraw(shapecolor color)const = 0 ;};class rectangle :public shape{public:    ..private:    virtual void dodraw(shapecolor color)const;};
阅读全文
1 0
原创粉丝点击