C++ 面向对象设计的五大基本原则,通常指的是 SOLID 原则。虽然 SOLID 最初是在 Java 和其他面向对象语言背景下提出的(由 Robert C. Martin 提出),但它们完全适用于 C++ 这样的多范式、支持面向对象编程的语言。
SOLID 是五个英文单词首字母的缩写,分别代表:
- S – Single Responsibility Principle (单一职责原则)
- O – Open/Closed Principle (开闭原则)
- L – Liskov Substitution Principle (里氏替换原则)
- I – Interface Segregation Principle (接口隔离原则)
- D – Dependency Inversion Principle (依赖倒置原则)
1. 单一职责原则(Single Responsibility Principle, SRP)
一个类应该只有一个引起它变化的原因。
换句话说,一个类只应负责一项职责。如果一个类承担了多个职责,那么这些职责就耦合在一起,修改其中一个可能会破坏另一个。
class Report {
public:
void generateReport() { /* 生成报告内容 */ }
void printReport() { /* 打印到控制台 */ }
void saveToFile(const std::string& filename) { /* 保存到文件 */ }
};
这个 Report 类既负责生成报告,又负责输出(打印或保存)。如果将来需要支持 PDF 输出、网络发送等,就需要频繁修改这个类。
改进(遵循 SRP):
class Report {
public:
std::string getContent() { return "Report content"; }
};
class Printer {
public:
void print(const std::string& content) { /* 打印逻辑 */ }
};
class FileSaver {
public:
void save(const std:: string& content, const std::string& filename) { /* 保存逻辑 */ }
};
每个类只做一件事,职责清晰,易于维护和扩展。
2. 开闭原则(Open/Closed Principle, OCP)
软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。
即:当需求变化时,应通过添加新代码来扩展功能,而不是修改已有代码。
C++ 示例(违反 OCP):
class Shape {
public:
enum Type { Circle, Square };
Type type;
};
double area(const Shape& s) {
if (s.type == Shape::Circle) {
// 计算圆面积
} else if (s.type == Shape::Square) {
// 计算正方形面积
}
}
每增加一种图形,就要修改 area() 函数,违反 OCP。
改进(遵循 OCP):
class Shape {
public:
virtual double area() const = 0;
virtual ~Shape() = default;
};
class Circle : public Shape {
double radius;
public:
Circle(double r) : radius(r) {}
double area() const override { return 3.14159 * radius * radius; }
};
class Square : public Shape {
double side;
public:
Square(double s) : side(s) {}
double area() const override { return side * side; }
};
// 使用
std::vector<std::unique_ptr<Shape>> shapes;
shapes.push_back(std::make_unique<Circle>(2.0));
shapes.push_back(std::make_unique<Square>(3.0));
for (const auto& shape : shapes) {
std::cout << shape->area() << std::endl;
}
新增图形只需继承 Shape 并实现 area(),无需修改现有代码。
3. 里氏替换原则(Liskov Substitution Principle, LSP)
子类型必须能够替换它们的基类型而不改变程序的正确性。
也就是说,任何使用基类的地方,都应该能无缝使用派生类,而不会导致错误或异常行为。
C++ 反例(违反 LSP):
class Rectangle {
protected:
int width, height;
public:
virtual void setWidth(int w) { width = w; }
virtual void setHeight(int h) { height = h; }
virtual int getArea() const { return width * height; }
};
class Square : public Rectangle {
public:
void setWidth(int w) override { width = height = w; }
void setHeight(int h) override { width = height = h; }
};
现在考虑这段代码:
void resize(Rectangle& r) {
r.setWidth(5);
r.setHeight(4);
assert(r.getArea() == 20); // 对 Square 会失败!
}
因为 Square 强制宽高相等,所以 resize 在传入 Square 时会出错。这违反了 LSP。
解决方案:
避免用继承建模“是正方形的矩形”这种数学关系。可以使用组合,或者重新设计类层次。
4. 接口隔离原则(Interface Segregation Principle, ISP)
客户端不应被迫依赖它们不使用的接口。
在 C++ 中,“接口”通常指纯虚类(抽象基类)。应将大而全的接口拆分为更小、更具体的接口。
反例(违反 ISP):
class Worker {
public:
virtual void work() = 0;
virtual void eat() = 0;
virtual void sleep() = 0;
};
class Human : public Worker {
void work() override { /* ... */ }
void eat() override { /* ... */ }
void sleep() override { /* ... */ }
};
class Robot : public Worker {
void work() override { /* ... */ }
void eat() override { /* 机器人不能吃! */ } // 被迫实现无意义方法
void sleep() override { /* ... */ }
};
改进(遵循 ISP):
class Workable {
public:
virtual void work() = 0;
};
class Eatable {
public:
virtual void eat() = 0;
};
class Sleepable {
public:
virtual void sleep() = 0;
};
class Human : public Workable, public Eatable, public Sleepable {
void work() override { /* ... */ }
void eat() override { /* ... */ }
void sleep() override { /* ... */ }
};
class Robot : public Workable {
void work() override { /* ... */ }
};
每个类只实现它真正需要的功能。
5. 依赖倒置原则(Dependency Inversion Principle, DIP)
高层模块不应依赖低层模块,二者都应依赖于抽象。 抽象不应依赖细节,细节应依赖抽象。
核心思想:通过抽象(如接口或抽象基类)解耦高层逻辑与底层实现。
反例(违反 DIP):
class Keyboard {};
class Monitor {};
class Computer {
Keyboard k;
Monitor m;
public:
void start() { /* 使用具体类 */ }
};
Computer 直接依赖具体硬件,难以替换或测试。
改进(遵循 DIP):
class IInputDevice {
public:
virtual void readInput() = 0;
};
class IOutputDevice {
public:
virtual void display() = 0;
};
class Keyboard : public IInputDevice {
void readInput() override { /* ... */ }
};
class Monitor : public IOutputDevice {
void display() override { /* ... */ }
};
class Computer {
std::unique_ptr<IInputDevice> input;
std::unique_ptr<IOutputDevice> output;
public:
Computer(std::unique_ptr<IInputDevice> in, std::unique_ptr<IOutputDevice> out)
: input(std::move(in)), output(std::move(out)) {}
void start() {
input->readInput();
output->display();
}
};
现在 Computer 依赖抽象接口,可以轻松替换输入/输出设备(如换成触摸屏、语音识别等),也便于单元测试(可注入 Mock 对象)。
总结
| 原则 | 核心思想 | C++ 实践要点 |
|---|---|---|
| SRP | 一个类一个职责 | 拆分类,避免多功能耦合 |
| OCP | 扩展开放,修改关闭 | 多用抽象基类 + 多态 |
| LSP | 子类可替换父类 | 继承需符合“行为子类型” |
| ISP | 小而专注的接口 | 避免胖接口,使用多重继承拆分 |
| DIP | 依赖抽象而非具体 | 通过接口/抽象类解耦 |
这些原则不是教条,而是指导我们写出高内聚、低耦合、易维护、可扩展的 C++ 代码的宝贵经验。在实际项目中,应结合具体场景灵活应用。

被折叠的 条评论
为什么被折叠?



