C++中区别接口继承与实现继承

转载自大佬的博客

不同于Objective C或者Java,C++中的继承接口和实现继承是同一个语法过程。 当你public继承一个类时,接口是一定会被继承的,你可以选择子类是否应当继承实现:

  • 不继承实现,只继承方法接口:纯虚函数。
  • 继承方法接口,以及默认的实现:虚函数。
  • 继承方法接口,以及强制的实现:普通函数。

一个例子

为了更加直观地讨论接口继承和实现继承的关系,我们还是来看一个例子:RectEllipse都继承自Shape

class Shape{
public:
    // 纯虚函数
    virtual void draw() const = 0;
    // 不纯的虚函数,impure...
    virtual void error(const string& msg);
    // 普通函数
    int id() const;
};
class Rect: public Shape{...};
class Ellipse: public Shape{...};
  • draw()是一个纯虚函数,子类必须重新声明draw方法,同时父类不给任何实现。
  • error()是一个普通的虚函数(注意,也要在子类中重新声明),子类可以提供一个error方法,也可以使用默认的实现。
  • id()是一个普通函数,子类继承了这个接口,以及强制的实现方式。

危险的默认实现

默认实现通常是子类中共同逻辑的抽象,显式地规约了子类的共同特性,避免了代码重复,方便了以后的增强,也便于长期的代码维护。 然而有时候提供默认实现是危险的,因为你不可预知会有怎样的子类添加进来。例如一个Airplane类以及它的几个Model子类:

class Airplane{
public:
    virtual void fly(){
        // default fly code
    }
};
class ModelA: public Airplane{...};
class ModelB: public Airplane{...};

不难想象,我们写父类Airplane时,其中的fly是针对ModelA和ModelB实现了通用的逻辑。如果有一天我们加入了ModelC却忘记了重写fly方法:

class ModelC: public Airplane{...};
Airplane* p = new ModelC;
p->fly();

虽然ModelC忘记了重写fly方法,但代码仍然成功编译了!这可能会引发灾难。。这个设计问题的本质是普通虚函数提供了默认实现,而不管子类是否显式地声明它需要默认实现。

安全的默认实现

我们可以用另一个方法来给出默认实现,而把fly声明为纯虚函数,这样既能要求子类显式地重新声明一个fly,当子类要求时又能提供默认的实现。

class Airplane{
public:
    virtual void fly() = 0;
protected:
    void defaultFly(){...}
}
class ModelA: public Airplane{
public:
    virtual void fly(){defaultFly();}
}
class ModelB: public Airplane{
public:
    virtual void fly(){defaultFly();}
}

我们在这里将defaultFly()声明为protected,这样就只有它的子类可以访问这个函数了,我们将fly声明为纯虚函数,这样我们就必须在它的子类中重新声明并实现它。通过这种机制,我们被强制重新实现fly(检查我们的需要)。同时由于default()的存在,我们仍然能够达到默认继承的效果。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值