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 对象时,结果不符合预期,这就违反了里氏替换原则。

总结

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

相关推荐
摇滚侠6 小时前
《计算机是如何工作的:人人都能懂的计算机软硬件工作原理》第 9 章 机器码与汇编语言 阅读笔记 9
java·开发语言·笔记
Cc.Y6 小时前
Java零基础入门:深入Java核心:文件IO与序列化全解
java·开发语言·python
xier_ran6 小时前
【infra之路】AI 编译器学习笔记(六):重拾 C++——lambda 与 MLIR 中的回调
c++·笔记·学习
jimy18 小时前
c++的 Type Traits (一)
开发语言·c++
别动我齐刘海8 小时前
Linux 系统调试学习2
linux·运维·c++·人工智能·学习·机器学习·机器人
Web极客码8 小时前
Python Ollama 本地 Agent 入门与订单查询实践
开发语言·jvm·python
kimnoic9 小时前
PHP 8.2的DNF类型怎么用才规范
开发语言·php
_Evan_Yao9 小时前
前后端联调怎么排查问题?从 Network 到后端日志,一步步定位错误
开发语言·后端·团队开发·需求分析
hudd6689 小时前
JavaEE 初阶 - 多线程(进阶)
java·开发语言·java-ee
小白快快跑哦9 小时前
python-字符串全解(六):正则表达式-前后相断言
开发语言·python·正则表达式·前后相断言