zoukankan      html  css  js  c++  java
  • 接口继承和实现继承

    设计类(class)的时候,你可能会干下面这几件事情:

    1.只让继承类(derived class)继承成员函数的接口;

    2.让derived class同时继承函数的接口和实现,但又能覆写所继承的实现;

    3.同时继承函数的接口和实现,但不允许覆写任何东西;

    以下面的类为例,来解释上面的三种实现:

    class Shape
    {
        public:
            virtual void draw() const = 0;
            virtual void error(const std::string& msg);
            int objectID() const;
            ...
    };
    
    class Rectangle: public Shape{...};
    class Ellipse: public Shape{...};
    

    对于draw函数,其实我们是可以调用的:如下:

    Shape *ps = new Shpae;               //Error! Shape is abstract
    Shape *ps1 = new Rectangle;         //ok
    ps1->draw();                         //call Rectangle::draw
    Shape *ps2 = new Ellipse;            //ok
    ps2->draw();                         //Ellipse::draw
    ps1->Shape::draw();                  //Shape::draw
    ps2->Shpae::draw();                  //Shape::draw

    对于Shape::error这个函数,为非纯虚函数,他告诉继承类,你如果不想自己写一个,则会调用基类的缺省版本。

    这种允许非纯虚函数的双面性行为,可能造成危险。例如,如果有个航空公司,该公司有A型和B型两种飞机,两者以相同的方式飞行,设计继承体系如下:

    class Airport    {...};
    class Airplane
    {
       public:
            virtual void fly(const Airport &destination);
            ...    
    };    
    
    void Airplane::fly(const Airport &destination)
    {
     //fly to the destination.   
    }
    
    class ModelA: public Airplane {...};
    class ModelB: public Airplane {...};

    现在,假设该公司购买了一种新式飞机C,飞行模式有些不同,该公司的程序员在继承体系中针对这种飞机添加了一个class,假如他们忘记了定义其fly函数:

    class ModelC: public Airplane
    {
       ...//not implementing fly() 
    };

    调用的时候,你会采用如下的操作:

    Airport BCIA(...);        //Beijing Capital International Airport;
    Airplane *pa = new ModelC;
    ...
    pa->fly(BCIA);            //call Airplane::fly

    上面的程序试图以ModelA和ModelB的飞行方式来飞ModelC,用喷气式飞机的方式来飞螺旋桨飞机,要出人命啊。

    问题不在于Airplance::fly有缺省行为,而在于ModelC在未说明“我要”的情况下就继承了该缺省行为。

    我们可以避免这种做法:

    class Airplane
    {
       public:
       virtual void fly(const Airport &destination) = 0;
       ...
       protected:
       void defaultFly(const Airport &destination);  
    };
    void Airplane::defaultFly(const Airport &destination)
    {
     //implementation of flying.   
    }

    将Airplane::fly改为一个pure virtual函数,这样的话,每个继承类都要自己实现fly:

    class ModelA: public Airplane
    {
       public:
       virtual void fly(const Airport &destination)
       {
           defaultFly(destination);
       }
       ...  
    };
    
    class ModelB: public Airplane
    {
       public:
       virtual void fly(const Airport &destination)
       {
          defaultFly(destination);
       }    
       ...
    };    

    现在ModelC必须自己实现fly函数来,嘿嘿:

    class ModelC: public Airplane
    {
       virtual void fly(const Airport &destination);
       ... 
    };
    
    void ModelC::fly(const Airport &destination)
    {
      //ModelC's way.
    }

    这种方式可能因为使用者的复制粘贴而错误,但是比之前有了保障。至于Airplane::defaultFly,现在设置成了protected,因为他是继承类的实现方式,用户只在意飞机能不能飞,不在意怎么飞的。

    当然,现在这种方式也有个问题,多定义了一个defaultFly,可能会导致命名空间污染的问题。我们可以用下面这种方式,就是纯虚函数必须在继承类中重新声明,但是他们可以拥有自己的实现。例如:

    class Airplane
    {
        public:
            virtual void fly(const Airport &destination) = 0;
    };
    void Airplane::fly(const Airport &destination)
    {
        //do flying;
    }
    
    class ModelA: public Airplane
    {
        public:
            virtual void fly(const Airport &destination)
            {
                Airplane::fly(destination);
            }
            ...
    };
    
    class ModelB: public Airplane
    {
        public:
            virtual void fly(const Airport &destination)
            {
                Airplane::fly(destination);
                ...
             }
    };
    
    class ModelC: public Airplane
    {
        public:
            virtual void fly(const Airport &destination);
            ...
    };
    void ModelC::fly(const Airport &destination)
    {
       //do ModelC's flying;
    }    

    最后,对于objectID这个非虚函数,意味着在继承类中,不需要对其进行修改。所以在继承类中不建议重新定义。这个会在下个议题中阐述。

      

  • 相关阅读:
    利用crontab每天定时备份MySQL数据库
    MySQL基本SQL语句之数据插入、删除数据和更新数据
    Vim命令合集
    ubuntu下手动安装php-amqp模块教程
    变量作用域(总结篇)
    变量作用域(示例篇)
    定义一个函数
    正则表达式
    re模块中常用功能函数
    python内置函数
  • 原文地址:https://www.cnblogs.com/edmundli/p/4444538.html
Copyright © 2011-2022 走看看