C++设计模式:面向对象的八大设计原则之四

里氏替换原则(Liskov Substitution Principle,LSP)是面向对象设计中的一个重要原则,它指出子类必须能够替换它的基类,并且程序的行为不会发生改变。也就是说,在任何使用基类对象的地方,都可以透明地使用其子类对象。继承不仅是代码复用的手段,更重要的是表达了类型的抽象,子类是对基类的一种特殊化。

下面通过几个 C++ 代码示例来详细讲解里氏替换原则。

示例一:简单的继承与替换

首先,我们定义一个基类 Shape 表示形状,然后派生出子类 Rectangle(矩形)和 Square(正方形)。

cpp 复制代码
#include <iostream>



// 基类:形状

class Shape {

public:

    virtual double area() const = 0;

    virtual ~Shape() {}

};



// 子类:矩形

class Rectangle : public Shape {

private:

    double width;

    double height;



public:

    Rectangle(double w, double h) : width(w), height(h) {}

    double area() const override {

        return width * height;

    }

};



// 子类:正方形

class Square : public Shape {

private:

    double side;



public:

    Square(double s) : side(s) {}

    double area() const override {

        return side * side;

    }

};



// 计算形状面积的函数

void printArea(const Shape& shape) {

    std::cout << "Area: " << shape.area() << std::endl;

}



int main() {

    Rectangle rect(3, 4);

    Square square(5);



    // 可以用子类对象替换基类对象

    printArea(rect);

    printArea(square);



    return 0;

}    

代码解释

Shape 是一个抽象基类,定义了纯虚函数 area(),用于计算形状的面积。

Rectangle 和 Square 是 Shape 的子类,分别实现了 area() 函数。

printArea 函数接受一个 Shape 类型的引用作为参数,在 main 函数中,我们可以将 Rectangle 和 Square 对象传递给 printArea 函数,这体现了子类可以替换基类的特性。

示例二:违反里氏替换原则的情况

有时候,如果子类的行为与基类的预期行为不一致,就会违反里氏替换原则。例如,我们对上面的 Rectangle 和 Square 类进行修改,添加 setWidth 和 setHeight 方法。

cpp 复制代码
#include <iostream>



// 基类:矩形

class Rectangle {

protected:

    double width;

    double height;



public:

    Rectangle(double w, double h) : width(w), height(h) {}

    virtual void setWidth(double w) { width = w; }

    virtual void setHeight(double h) { height = h; }

    double area() const {

        return width * height;

    }

};



// 子类:正方形

class Square : public Rectangle {

public:

    Square(double s) : Rectangle(s, s) {}

    void setWidth(double w) override {

        width = w;

        height = w;

    }

    void setHeight(double h) override {

        width = h;

        height = h;

    }

};



// 调整矩形尺寸并打印面积的函数

void resizeAndPrint(Rectangle& rect) {

    rect.setWidth(5);

    rect.setHeight(10);

    std::cout << "Area: " << rect.area() << std::endl;

}



int main() {

    Rectangle rect(3, 4);

    Square square(5);



    // 对于矩形,预期结果是 5 * 10 = 50

    resizeAndPrint(rect);



    // 对于正方形,结果不符合预期,因为正方形的宽和高总是相等的

    resizeAndPrint(square);



    return 0;

}

代码解释

在这个例子中,Square 类继承自 Rectangle 类,但是 Square 类的 setWidth 和 setHeight 方法会同时修改宽和高,这与 Rectangle 类的预期行为不一致。

resizeAndPrint 函数期望传入的 Rectangle 对象可以独立地设置宽和高,但是当传入 Square 对象时,结果不符合预期,这就违反了里氏替换原则。

总结

里氏替换原则强调了继承关系中类型的兼容性和行为的一致性。在设计类和继承体系时,要确保子类能够完全替换基类,并且不会破坏程序的正确性。如果子类的行为与基类的预期行为不一致,就可能需要重新考虑类的设计,例如使用组合而不是继承。

相关推荐
m0_748250034 分钟前
C++ 官方文档与标准
开发语言·c++
zh_xuan19 分钟前
kotlin 类继承的语法2
开发语言·kotlin
matlabgoodboy30 分钟前
程序代做python代编程matlab定制代码编写C++代写plc设计java帮做
c++·python·matlab
魅影骑士001039 分钟前
柯里化函数
后端·设计模式
DYS_房东的猫1 小时前
《 C++ 零基础入门教程》第6章:模板与 STL 算法 —— 写一次,用万次
开发语言·c++·算法
诗意地回家1 小时前
淘宝小游戏反编译
开发语言·前端·javascript
wangkay881 小时前
【Java 转运营】Day04:抖音新号起号前准备全指南
java·开发语言·新媒体运营
点云SLAM1 小时前
C++ 静态初始化顺序问题(SIOF)和SLAM / ROS 工程实战问题
开发语言·c++·slam·静态初始化顺序问题·工程实战技术·c++static 关键字
D3bugRealm1 小时前
MATLAB解决物理问题:从基础运动学到进阶力学的实战指南
开发语言·其他·matlab
小李独爱秋1 小时前
计算机网络经典问题透视:TLS协议工作过程全景解析
运维·服务器·开发语言·网络协议·计算机网络·php