入门设计原则C++实现四:里氏替换原则

里氏替换原则是面向对象设计的基本原则之一,它规定子类应当可以替换其基类并保持程序的正确运行。错误的继承关系如几维鸟不能飞行的例子违反了该原则,正确的做法是将不同行为归类到合适的父类中,如将飞行能力赋予鸟类,将行走能力赋予动物类。遵循里氏替换原则可以确保代码的稳定性和可扩展性。
摘要由CSDN通过智能技术生成

派生类的对象能够替换其基类的对象被使用

定义

里氏替换原则主要阐述了有关继承的一些原则,也就是什么时候应该使用继承,什么时候不应该使用继承,以及其中蕴含的原理。里氏替换原是继承复用的基础,它反映了基类与子类之间的关系,是对开闭原则的补充,是对实现抽象化的具体步骤的规范

举个例子,比如有一个基类Animal和一个派生类Cat,程序中任何使用Animal对象的地方,我们都可以放Cat对象上去(派生类的对象能够替换子类的对象),而完全不扰乱程序的逻辑(程序里关于Animal的假设,Cat都不能打破)。

更通俗一点描述就是,假如我们给Animal设定方法,可以设定一个吃(), 这个没问题,凡是动物都会吃东西,猫也会吃东西。但是如果给动物设定方法:走路(),就不太好了,因为不是所有的动物都会走路,比如鱼。假如Animal类存在走路这个方法,且Animal对象调用了该方法,此时再用Cat对象替换Animal对象就不符合常识了,此时违背了里氏替换原则。

实现方法

里氏替换原则通俗来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。也就是说:子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法(除非基类中的纯虚函数必须要重写,虚函数可以重写,普通函数不能或者尽量不要重写)

  • 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法

  • 子类中可以增加自己特有的方法

  • 当子类的方法重载父类的方法时,方法的前置条件(即方法的输入参数)要比父类的方法更宽松

  • 当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件(即方法的的输出/返回值)要比父类的方法更严格或相等

案例分析

[示例1] 几维鸟不是鸟–破坏原则案例

分析:鸟一般都会飞行,如燕子的飞行速度大概是每小时 120 千米。但是新西兰的几维鸟由于翅膀退化无法飞行。假如类的设计如下:基类为鸟(具有飞行速度这一属性且v大于0),燕子和几维鸟继承自鸟(实际上不是所有鸟的v都大于零,因此几维鸟对象替换基类对象时可能会出错)。

#include <iostream>
class Bird {
public:
    float flySpeed; //不符合里氏替换原则
    void setSpeed(float v) {
        flySpeed = v;
    }
    float getFlyTime(float dis) {
        return dis/flySpeed;
    }
};
class Swallow : public Bird {

};
class BrownKiwi : public Bird {

};
int main(){
    Bird* b1 = new Swallow; // &b1 是
    Bird* b2 = new BrownKiwi;

    b1->setSpeed(300);
    b2->setSpeed(0);

    std::cout<<"b1 fly time is: " << b1->getFlyTime(300)<<std::endl;
    std::cout<<"b2 fly time is: " << b2->getFlyTime(300)<<std::endl;

    return 1;
}

[示例2]

正确的做法是:取消几维鸟原来的继承关系,定义鸟和几维鸟的更一般的父类,如动物类,它们都有呼吸的能力,鸟类继承自动物类,燕子继承自鸟类;几维鸟继承自动物类。

#include <iostream>

class Animal{
public:
    float runSpeed;
    virtual void setRunSpeed(float v) {
        runSpeed = v;
    }
    virtual float getRunTime(float dis) {
        return dis/runSpeed;
    }
};

class Bird : public Animal{
public:
    float flySpeed;
    virtual void setSpeed(float v) {
        flySpeed = v;
    }
    virtual float getFlyTime(float dis) {
        return dis/flySpeed;
    }
};

class Swallow : public Bird {

};

class BrownKiwi : public Animal {

};

int main(){
    Bird* b1 = new Swallow; // &b1 是
    Animal* b2 = new BrownKiwi;

    b1->setSpeed(300);
    b1->setRunSpeed(0.01);
    b2->setRunSpeed(0.2);

    std::cout<<"b1 fly time is: " << b1->getFlyTime(300)<<std::endl;
    std::cout<<"b1 run time is: " << b1->getRunTime(300)<<std::endl;
    std::cout<<"b2 fly time is: " << b2->getRunTime(300)<<std::endl;

    return 1;
}


  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值