C++的核心原则和设计模式规范(全)
面向对象编程的四大核心原则
1. 封装
核心思想:将数据(属性)和操作数据的方法(函数)捆绑在一起,并隐藏对象的内部实现细节。对象只对外提供一个可控的访问接口。
#include <iostream>
#include <string>
using namespace std;
class BankAccount {
private:
string accountHolder;
string accountNumber;
double balance; // 私有数据,外部无法直接访问
public:
// 构造函数
BankAccount(string holder, string accNumber, double initialBalance = 0) {
accountHolder = holder;
accountNumber = accNumber;
balance = initialBalance;
}
// 公开接口 - 存款
void deposit(double amount) {
if (amount > 0) {
balance += amount;
cout << "存款成功,当前余额: " << balance << endl;
} else {
cout << "存款金额必须大于0" << endl;
}
}
// 公开接口 - 取款
void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
cout << "取款成功,当前余额: " << balance << endl;
} else {
cout << "取款金额无效或余额不足" << endl;
}
}
// 公开接口 - 获取余额(只读)
double getBalance() const {
return balance;
}
// 公开接口 - 获取账户信息
string getAccountInfo() const {
return "账户持有人: " + accountHolder + ", 账户号码: " + accountNumber;
}
};
int main() {
BankAccount account("张三", "123456789", 1000);
account.deposit(500); // ✅ 通过公开接口操作
account.withdraw(200); // ✅ 通过公开接口操作
// account.balance = 10000; // ❌ 编译错误:无法访问私有成员
cout << "当前余额: " << account.getBalance() << endl; // ✅ 通过getter访问
cout << account.getAccountInfo() << endl;
return 0;
}
好处:
1.提高安全性:防止外部代码随意修改内部数据,只能通过你提供的方法来修改,你可以在方法中添加验证逻辑。
2.降低复杂度:使用者无需关心内部实现,只需关注接口。
3.易于维护:内部实现可以自由更改,只要对外接口不变,就不会影响其他代码。
2. 抽象
核心思想:只向用户展示必要的、关键的信息,而隐藏不必要的实现细节。它是封装的更高层次,通过抽象类和接口来实现。
#include <iostream>
#include <string>
using namespace std;
// 抽象类 - 包含纯虚函数
class Vehicle {
public:
// 纯虚函数 - 接口声明,没有实现
virtual void startEngine() = 0;
virtual void stopEngine() = 0;
virtual double getFuelLevel() const = 0;
virtual ~Vehicle() {} // 虚析构函数
};
// 具体实现类
class Car : public Vehicle {
private:
string engineStatus;
double fuelLevel;
bool sparkPlugsOk;
// 私有方法 - 隐藏实现细节
bool checkSparkPlugs() {
return sparkPlugsOk;
}
public:
Car() : engineStatus("off"), fuelLevel(100), sparkPlugsOk(true) {}
void startEngine() override {
if (checkSparkPlugs() && fuelLevel > 0) {
engineStatus = "on";
fuelLevel -= 5;
cout << "🚗 汽车引擎已启动" << endl;
} else {
cout << "无法启动引擎" << endl;
}
}
void stopEngine() override {
engineStatus = "off";
cout << "🚗 汽车引擎已关闭" << endl;
}
double getFuelLevel() const override {
return fuelLevel;
}
};
class Motorcycle : public Vehicle {
private:
bool engineRunning;
double fuel;
public:
Motorcycle() : engineRunning(false), fuel(80) {}
void startEngine() override {
engineRunning = true;
fuel -= 3;
cout << "🏍️ 摩托车引擎已启动" << endl;
}
void stopEngine() override {
engineRunning = false;
cout << "🏍️ 摩托车引擎已关闭" << endl;
}
double getFuelLevel() const override {
return fuel;
}
};
int main() {
// Vehicle v; // ❌ 错误:不能实例化抽象类
Vehicle* car = new Car();
Vehicle* motorcycle = new Motorcycle();
car->startEngine(); // ✅ 用户只知道简单接口
cout << "汽车油量: " << car->getFuelLevel() << "%" << endl;
motorcycle->startEngine(); // ✅ 统一接口,不同实现
cout << "摩托车油量: " << motorcycle->getFuelLevel() << "%" << endl;
delete car;
delete motorcycle;
return 0;
}
好处:
1.简化复杂性:让程序员可以专注于高级逻辑,而不是底层实现。
2.降低耦合:代码模块之间只通过抽象的接口交互,减少依赖。
封装 vs. 抽象:
封装是关于“如何隐藏数据和控制访问”。
抽象是关于“如何隐藏实现细节并提供简化接口”。可以认为抽象是通过封装等手段实现的更高层次的设计思想。
3. 继承
核心思想:允许一个类(子类/派生类)基于另一个类(父类/基类)来创建。子类会自动获得父类的属性和方法,并可以添加自己特有的属性和方法。
#include <iostream>
#include <string>
using namespace std;
// 基类(父类)
class Animal {
protected:
string name;
int age;
public:
Animal(string n, int a) : name(n), age(a) {}
// 虚函数 - 允许子类重写
virtual void eat() {
cout << name << " 正在吃东西" << endl;
}
virtual void sleep() {
cout << name << " 正在睡觉" << endl;
}
virtual void makeSound() {
cout << "动物发出声音" << endl;
}
virtual ~Animal() {} // 虚析构函数
};
// 派生类(子类)
class Dog : public Animal {
private:
string breed;
public:
Dog(string n, int a, string b) : Animal(n, a), breed(b) {}
// 重写基类方法
void makeSound() override {
cout << name << " 汪汪叫!" << endl;
}
// 子类特有方法
void fetch() {
cout << name << " 正在接飞盘" << endl;
}
void displayInfo() {
cout << name << " 是一只 " << breed << ",今年 " << age << " 岁" << endl;
}
};
class Cat : public Animal {
private:
string color;
public:
Cat(string n, int a, string c) : Animal(n, a), color(c) {}
void makeSound() override {
cout << name << " 喵喵叫!" << endl;
}
void climbTree() {
cout << name << " 正在爬树" << endl;
}
void displayInfo() {
cout << name << " 是一只 " << color << "的猫,今年 " << age << " 岁" << endl;
}
};
int main() {
Dog dog("旺财", 3, "金毛");
Cat cat("咪咪", 2, "白色");
dog.eat(); // ✅ 继承自Animal
dog.makeSound(); // ✅ 重写的方法
dog.fetch(); // ✅ 子类特有方法
dog.displayInfo();
cout << endl;
cat.sleep(); // ✅ 继承自Animal
cat.makeSound(); // ✅ 重写的方法
cat.climbTree(); // ✅ 子类特有方法
cat.displayInfo();
return 0;
}
好处:
1.代码复用:子类可以直接复用父类的代码,无需重写。
2.建立类之间的层次关系:使代码结构更清晰,更符合现实世界的逻辑。
4. 多态
核心思想:意为“多种形态”。它允许使用一个统一的接口来操作不同的底层对象。具体来说,同一个方法在不同的对象上会有不同的行为,通常通过继承和方法重写来实现。
#include <iostream>
#include <vector>
#include <memory> // 智能指针
using namespace std;
// 使用前面定义的Animal, Dog, Cat类
void animalConcert(const vector<Animal*>& animals) {
cout << "=== 动物音乐会 ===" << endl;
for (const auto& animal : animals) {
animal->makeSound(); // 多态:同一接口,不同行为
}
}
void takeCareOfAnimal(Animal* animal) {
cout << "正在照顾动物..." << endl;
animal->eat();
animal->sleep();
animal->makeSound();
cout << "---" << endl;
}
int main() {
vector<Animal*> animals;
// 创建不同种类的动物
animals.push_back(new Dog("旺财", 3, "金毛"));
animals.push_back(new Cat("咪咪", 2, "白色"));
animals.push_back(new Dog("小黑", 4, "拉布拉多"));
// 多态演示
animalConcert(animals);
cout << "\n=== 动物护理 ===" << endl;
for (auto animal : animals) {
takeCareOfAnimal(animal);
}
// 清理内存
for (auto animal : animals) {
delete animal;
}
return 0;
}
好处:
1.接口统一:编写更通用、更灵活的代码。
2.可扩展性:添加新的子类(如 Bird)时,无需修改处理 Animal 的通用代码。
其他非常重要的关联原则和概念
除了四大支柱,在现代软件工程中,以下原则也至关重要,它们指导我们如何更好地使用OOP。
1. 组合优于继承
核心思想:尽量使用组合(包含关系)而不是继承(是一个关系)来复用代码。
组合:一个类包含另一个类的对象作为其成员变量。好比“有一个”的关系,例如 Car 类“有一个” Engine(引擎)对象。
继承会带来很强的耦合性,父类的任何改动都可能影响所有子类,这会使设计变得僵化。组合更加灵活,可以在运行时动态改变行为,并且降低了类之间的依赖。
设计原则:如果关系是“有一个”(如汽车有引擎),用组合;如果是“是一个”(如狗是动物),才考虑用继承。
#include <iostream>
#include <memory>
using namespace std;
// 使用组合而不是继承
class Engine {
public:
void start() {
cout << "燃油引擎启动" << endl;
}
void stop() {
cout << "燃油引擎关闭" << endl;
}
};
class ElectricMotor {
public:
void start() {
cout << "电动马达启动" << endl;
}
void stop() {
cout << "电动马达关闭" << endl;
}
};
class Battery {
private:
double chargeLevel;
public:
Battery() : chargeLevel(100) {}
void charge() {
chargeLevel = 100;
cout << "电池已充满" << endl;
}
double getChargeLevel() const {
return chargeLevel;
}
};
// 通过组合构建汽车
class Car {
private:
// 组合:拥有其他类的对象
unique_ptr<Engine> engine;
unique_ptr<Battery> battery;
string carType;
public:
// 构造函数注入不同的动力系统
Car(unique_ptr<Engine> eng, unique_ptr<Battery> bat, string type)
: engine(move(eng)), battery(move(bat)), carType(type) {}
void start() {
cout << carType << "启动: ";
if (engine) {
engine->start();
}
}
void stop() {
cout << carType << "关闭: ";
if (engine) {
engine->stop();
}
}
void checkBattery() {
if (battery) {
cout << "电池电量: " << battery->getChargeLevel() << "%" << endl;
}
}
};
int main() {
// 创建不同类型的汽车(通过组合)
auto gasCar = make_unique<Car>(
make_unique<Engine>(),
make_unique<Battery>(),
"燃油车"
);
auto hybridCar = make_unique<Car>(
make_unique<Engine>(),
make_unique<Battery>(),
"混合动力车"
);
gasCar->start();
gasCar->checkBattery();
cout << endl;
hybridCar->start();
hybridCar->checkBattery();
return 0;
}
2. SOLID 原则
S - 单一职责原则:一个类应该只有一个引起它变化的原因。即一个类只负责一项功能。
O - 开放封闭原则:软件实体(类、模块、函数)应该对扩展开放,对修改关闭。意思是,当需要添加新功能时,应该通过扩展(如继承、多态)来实现,而不是修改已有的代码。
L - 里氏替换原则:子类必须能够替换掉它们的父类,而不破坏程序。这是多态能正确工作的基础。确保继承关系是真正合理的“是一个”关系。
I - 接口隔离原则:不应该强迫客户依赖于它们不用的方法。即使用多个专门的接口,而不是一个庞大臃肿的总接口。
D - 依赖倒置原则:
高层模块不应该依赖于低层模块,二者都应该依赖于抽象。
抽象不应该依赖于细节,细节应该依赖于抽象。
简单说:要依赖于接口(抽象),而不是具体的实现。
#include <iostream>
#include <string>
using namespace std;
// 违反单一职责原则的类(承担太多责任)
class EmployeeBad {
private:
string name;
double salary;
public:
EmployeeBad(string n, double s) : name(n), salary(s) {}
// 计算工资 - 财务职责
double calculatePay() {
return salary * 0.9; // 扣除税费
}
// 数据库操作 - 数据持久化职责
void saveToDatabase() {
cout << "将 " << name << " 的信息保存到数据库" << endl;
}
// 生成报告 - 报告生成职责
void generateReport() {
cout << "为 " << name << " 生成工作报告" << endl;
}
};
// 遵循单一职责原则的重构
class Employee {
private:
string name;
double salary;
public:
Employee(string n, double s) : name(n), salary(s) {}
string getName() const { return name; }
double getSalary() const { return salary; }
};
// 专门的工资计算类
class PayCalculator {
public:
double calculatePay(const Employee& emp) const {
return emp.getSalary() * 0.9; // 扣除税费
}
};
// 专门的数据访问类
class EmployeeRepository {
public:
void save(const Employee& emp) const {
cout << "将 " << emp.getName() << " 的信息保存到数据库" << endl;
}
};
// 专门的报告生成类
class ReportGenerator {
public:
void generateReport(const Employee& emp) const {
cout << "为 " << emp.getName() << " 生成工作报告" << endl;
}
};
int main() {
Employee emp("李四", 5000);
PayCalculator payCalc;
EmployeeRepository repository;
ReportGenerator reportGen;
cout << "应发工资: " << payCalc.calculatePay(emp) << endl;
repository.save(emp);
reportGen.generateReport(emp);
return 0;
}
3.接口隔离原则
#include <iostream>
using namespace std;
// 庞大的接口 - 违反接口隔离原则
class IWorker {
public:
virtual void work() = 0;
virtual void eat() = 0;
virtual void sleep() = 0;
virtual void code() = 0;
virtual void design() = 0;
virtual void test() = 0;
virtual ~IWorker() {}
};
// 遵循接口隔离原则:拆分成专门的接口
class IWorkable {
public:
virtual void work() = 0;
virtual ~IWorkable() {}
};
class IEatable {
public:
virtual void eat() = 0;
virtual ~IEatable() {}
};
class ICodeable {
public:
virtual void code() = 0;
virtual ~ICodeable() {}
};
class IDesignable {
public:
virtual void design() = 0;
virtual ~IDesignable() {}
};
// 具体类只需要实现需要的接口
class Programmer : public IWorkable, public ICodeable {
public:
void work() override {
cout << "程序员开始工作" << endl;
}
void code() override {
cout << "程序员在写代码" << endl;
}
};
class Designer : public IWorkable, public IDesignable {
public:
void work() override {
cout << "设计师开始工作" << endl;
}
void design() override {
cout << "设计师在做设计" << endl;
}
};
int main() {
Programmer prog;
Designer designer;
prog.work();
prog.code();
// prog.design(); // ❌ 程序员不需要设计方法
cout << endl;
designer.work();
designer.design();
// designer.code(); // ❌ 设计师不需要编码方法
return 0;
}
设计规范
1.代码风格:
遵循一致的命名规范(如驼峰命名法、下划线命名法)。
使用有意义的变量名、函数名和类名。
保持函数短小,功能单一。
限制行的长度(通常80或120字符)。
一致地使用缩进和空格。
// 类型名 - PascalCase
class MyClass {};
struct MyStruct {};
using MyAlias = int;
// 变量名 - snake_case
int student_count;
std::string file_name;
// 常量 - kPascalCase或UPPER_CASE
const int kMaxSize = 100;
constexpr double PI = 3.14159;
// 函数名 - PascalCase或snake_case
void CalculateTotal(); // 推荐
void calculate_total(); // 也可接受
2.头文件管理:
头文件应包含防止重复包含的守卫(#pragma once 或 #ifndef)。
头文件只包含必要的声明,避免包含不必要的头文件,使用前向声明减少依赖。
头文件应当自包含(即包含它所需要的所有头文件)。
// my_class.h
#ifndef MY_PROJECT_MY_CLASS_H
#define MY_PROJECT_MY_CLASS_H
#include <vector>
#include <string>
namespace my_project {
class MyClass {
public:
explicit MyClass(int initial_value);
void process_data();
int get_result() const { return result_; }
private:
int result_;
std::vector<int> data_;
};
} // namespace my_project
#endif // MY_PROJECT_MY_CLASS_H
3.类设计:
明确类的职责,遵循单一职责原则。
使用访问修饰符(public, protected, private)严格控制成员的可访问性。
优先使用组合 over 继承。
如果类包含动态分配的资源,应遵循Rule of Three/Five/Zero(提供拷贝构造函数、拷贝赋值运算符、析构函数,或使用智能指针避免手动管理)。
4.函数设计:
函数参数:优先使用const引用传递大型对象,避免值传递;输出参数使用指针或引用。
函数重载:当函数执行相似操作但参数不同时,使用重载。
默认参数:谨慎使用,避免与重载混淆。
class Container {
public:
// 明确的行为
void add_item(const Item& item);
void remove_item(int id);
bool contains(int id) const;
size_t size() const noexcept;
// 禁止拷贝但允许移动
Container(const Container&) = delete;
Container& operator=(const Container&) = delete;
Container(Container&&) noexcept = default;
Container& operator=(Container&&) noexcept = default;
private:
std::vector<Item> items_;
};
5.内存管理:
优先使用智能指针(unique_ptr, shared_ptr, weak_ptr)管理动态内存,避免手动new/delete。
// 优先使用智能指针
auto ptr = std::make_unique<MyClass>();
auto shared = std::make_shared<MyClass>();
// 避免裸new/delete
// 不好的做法: MyClass* obj = new MyClass();
// 好的做法: auto obj = std::make_unique<MyClass>();
6.常量正确性:
使用const修饰不改变的对象和成员函数,提高代码的可读性和安全性。
class MyClass {
public:
// const成员函数,不修改对象状态
int get_value() const { return value_; }
// 非const成员函数
void set_value(int v) { value_ = v; }
private:
int value_;
};
7.使用auto和类型推导
// 好的用法
auto iter = container.begin();
const auto& item = get_item();
auto result = calculate_complex_value();
// 避免过度使用
auto x = 42; // 可以,类型明确
auto y = get_something(); // 需要看函数签名
更多推荐


所有评论(0)