1. 现代C++设计模式概述
在C++11/14/17及后续版本中,语言特性的演进为传统设计模式带来了新的实现方式。作为一名长期使用C++进行系统开发的工程师,我发现现代C++特性让许多经典模式变得更加简洁和安全。智能指针、lambda表达式、移动语义等特性不仅改变了我们编写代码的方式,也重新定义了设计模式的最佳实践。
现代C++设计模式的核心优势在于:
- 更强的类型安全性
- 更简洁的实现方式
- 更好的线程安全保证
- 更灵活的组合能力
下面我将详细介绍10种特别适合现代C++的设计模式,包括它们的典型应用场景、现代实现方式以及实际项目中的使用技巧。
2. 单例模式 (Singleton Pattern)
2.1 现代实现方式
在C++11之前,实现线程安全的单例模式需要复杂的双重检查锁定机制。现在,我们可以利用static局部变量的线程安全初始化特性:
cpp复制class Singleton {
public:
static Singleton& getInstance() {
static Singleton instance; // 线程安全初始化
return instance;
}
// 删除拷贝构造函数和赋值运算符
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
private:
Singleton() = default; // 私有构造函数
~Singleton() = default;
};
2.2 实现要点解析
- 线程安全:C++11标准保证static局部变量的初始化是线程安全的
- 资源管理:依赖RAII机制,实例在程序结束时自动销毁
- 防拷贝:显式删除拷贝构造函数和赋值运算符,防止意外拷贝
提示:对于需要延迟初始化但又不总是需要使用的单例,可以考虑使用
std::optional或std::unique_ptr配合std::call_once实现按需创建。
3. 工厂方法模式 (Factory Method Pattern)
3.1 现代实现演变
传统工厂模式常返回原始指针,现代C++则倾向于使用智能指针:
cpp复制class Product {
public:
virtual ~Product() = default;
virtual void use() = 0;
};
class ConcreteProductA : public Product { /*...*/ };
class ConcreteProductB : public Product { /*...*/ };
using ProductPtr = std::unique_ptr<Product>;
ProductPtr createProduct(const std::string& type) {
if (type == "A") return std::make_unique<ConcreteProductA>();
if (type == "B") return std::make_unique<ConcreteProductB>();
return nullptr;
}
3.2 高级工厂模式技巧
- 注册式工厂:使用map存储创建函数,支持运行时动态注册
cpp复制std::unordered_map<std::string, std::function<ProductPtr()>> factoryMap;
template <typename T>
void registerProduct(const std::string& name) {
factoryMap[name] = []() { return std::make_unique<T>(); };
}
ProductPtr createProduct(const std::string& name) {
if (auto it = factoryMap.find(name); it != factoryMap.end())
return it->second();
return nullptr;
}
- 参数化工厂:支持带参数的构造函数
cpp复制template <typename... Args>
using ProductFactory = std::function<ProductPtr(Args...)>;
4. 策略模式 (Strategy Pattern)
4.1 传统与现代实现对比
传统策略模式需要定义策略接口和多个实现类,现代C++可以使用std::function简化:
cpp复制class Context {
std::function<void()> strategy_;
public:
void setStrategy(std::function<void()> strategy) {
strategy_ = std::move(strategy);
}
void executeStrategy() {
if (strategy_) strategy_();
}
};
// 使用示例
Context ctx;
ctx.setStrategy([]() { std::cout << "Strategy A\n"; });
ctx.executeStrategy();
4.2 策略模式的现代应用
- 算法选择:运行时切换排序/搜索算法
- 业务规则:根据不同地区应用不同的定价策略
- IO策略:在测试和生产环境使用不同的存储策略
注意:当策略逻辑非常复杂或需要维护状态时,仍建议使用传统的类层次结构实现。
5. 观察者模式 (Observer Pattern)
5.1 现代信号槽实现
现代C++可以使用std::function和容器实现轻量级观察者模式:
cpp复制class Subject {
std::vector<std::function<void(int)>> observers_;
public:
void attach(std::function<void(int)> observer) {
observers_.push_back(std::move(observer));
}
void notify(int data) {
for (auto& obs : observers_) {
obs(data);
}
}
};
// 使用示例
Subject subject;
subject.attach([](int data) { std::cout << "Observer 1: " << data << "\n"; });
subject.notify(42);
5.2 生命周期管理要点
- 弱引用:对于可能先于Subject销毁的Observer,使用
std::weak_ptr避免悬空引用 - 线程安全:在多线程环境中使用
std::mutex保护observers容器 - 性能优化:考虑使用
boost::signals2或事件队列实现异步通知
6. 构建者模式 (Builder Pattern)
6.1 流畅接口实现
现代C++构建者模式常采用流畅接口风格:
cpp复制class Pizza {
std::string size_;
std::vector<std::string> toppings_;
public:
class Builder {
Pizza pizza_;
public:
Builder& setSize(std::string size) {
pizza_.size_ = std::move(size);
return *this;
}
Builder& addTopping(std::string topping) {
pizza_.toppings_.push_back(std::move(topping));
return *this;
}
Pizza build() {
return std::move(pizza_);
}
};
};
// 使用示例
Pizza pizza = Pizza::Builder()
.setSize("Large")
.addTopping("Cheese")
.addTopping("Pepperoni")
.build();
6.2 构建者模式变体
- 分步验证:在build()方法中验证参数合法性
- 不可变对象:构建完成后返回const对象
- 可选参数:结合
std::optional处理可选构建参数
7. 依赖注入 (Dependency Injection)
7.1 现代实现方式
依赖注入是现代软件设计的重要原则,C++中常用构造函数注入:
cpp复制class Service {
public:
virtual void doWork() = 0;
virtual ~Service() = default;
};
class Client {
std::unique_ptr<Service> service_;
public:
Client(std::unique_ptr<Service> service)
: service_(std::move(service)) {}
void execute() { service_->doWork(); }
};
7.2 依赖注入框架
对于复杂项目,可以考虑使用依赖注入框架如Boost.DI:
cpp复制#include <boost/di.hpp>
namespace di = boost::di;
auto injector = di::make_injector(
di::bind<Service>().to<ConcreteService>()
);
auto client = injector.create<Client>();
client.execute();
8. 适配器模式 (Adapter Pattern)
8.1 新旧接口适配
适配器模式常用于集成遗留代码:
cpp复制// 旧接口
class LegacyPrinter {
public:
void printDocument(const char* doc) { /*...*/ }
};
// 新接口
class ModernPrinter {
public:
virtual void print(const std::string& doc) = 0;
};
// 适配器
class LegacyPrinterAdapter : public ModernPrinter {
LegacyPrinter legacyPrinter_;
public:
void print(const std::string& doc) override {
legacyPrinter_.printDocument(doc.c_str());
}
};
8.2 现代适配技巧
- lambda适配器:对于简单接口,可以直接用lambda包装
- 多接口适配:一个适配器可以实现多个目标接口
- 双向适配器:允许新旧系统相互调用
9. 状态模式 (State Pattern)
9.1 传统状态机实现
状态模式封装了状态相关的行为:
cpp复制class Context;
class State {
public:
virtual void handle(Context* context) = 0;
virtual ~State() = default;
};
class Context {
std::unique_ptr<State> state_;
public:
void setState(std::unique_ptr<State> state) {
state_ = std::move(state);
}
void request() {
if (state_) state_->handle(this);
}
};
9.2 C++17的替代方案
使用std::variant可以实现类型安全的状态机:
cpp复制struct StateA { void handle() { /*...*/ } };
struct StateB { void handle() { /*...*/ } };
using State = std::variant<StateA, StateB>;
class StateMachine {
State state_;
public:
void handle() {
std::visit([](auto& s) { s.handle(); }, state_);
}
template <typename T>
void transitionTo() {
state_ = T{};
}
};
10. 装饰器模式 (Decorator Pattern)
10.1 动态扩展功能
装饰器模式允许运行时添加功能:
cpp复制class Component {
public:
virtual void operation() = 0;
virtual ~Component() = default;
};
class ConcreteComponent : public Component { /*...*/ };
class Decorator : public Component {
protected:
std::unique_ptr<Component> component_;
public:
Decorator(std::unique_ptr<Component> comp)
: component_(std::move(comp)) {}
void operation() override {
component_->operation();
}
};
class ConcreteDecoratorA : public Decorator {
public:
using Decorator::Decorator;
void operation() override {
Decorator::operation();
// 添加额外行为
}
};
10.2 装饰器模式应用场景
- IO操作:添加缓冲、加密、压缩等装饰
- UI组件:动态添加边框、滚动条等装饰
- 中间件:在调用链中添加日志、缓存等装饰
11. 访问者模式 (Visitor Pattern)
11.1 传统实现方式
访问者模式适用于复杂对象结构的操作:
cpp复制class Element;
class ConcreteElementA;
class ConcreteElementB;
class Visitor {
public:
virtual void visit(ConcreteElementA* element) = 0;
virtual void visit(ConcreteElementB* element) = 0;
virtual ~Visitor() = default;
};
class Element {
public:
virtual void accept(Visitor& visitor) = 0;
virtual ~Element() = default;
};
class ConcreteElementA : public Element {
public:
void accept(Visitor& visitor) override {
visitor.visit(this);
}
};
// 具体访问者实现...
11.2 C++17的替代方案
使用std::variant和std::visit可以简化实现:
cpp复制using Element = std::variant<ConcreteElementA, ConcreteElementB>;
struct Visitor {
void operator()(ConcreteElementA& el) { /*...*/ }
void operator()(ConcreteElementB& el) { /*...*/ }
};
void process(Element& el) {
std::visit(Visitor{}, el);
}
12. 现代C++设计模式选择建议
在实际项目中应用设计模式时,我有以下几点经验分享:
-
优先组合而非继承:现代C++鼓励使用组合、
std::function、lambda等轻量级方式替代深度继承层次。 -
善用智能指针:
std::unique_ptr和std::shared_ptr极大简化了资源管理,但要注意所有权语义。 -
利用新特性:
std::optional:明确表达可选值std::variant:类型安全的联合体std::any:类型擦除容器- lambda表达式:轻量级策略实现
-
避免过度设计:只在确实需要解决特定问题时才使用设计模式,而不是为了模式而模式。
-
性能考量:了解各模式的性能特征,比如虚函数调用开销、动态分配成本等。
-
测试友好:设计模式应该使代码更易于测试,特别是依赖注入、策略等模式。
-
团队共识:确保团队成员对使用的模式有共同理解,必要时进行文档说明。
在实际项目中,我经常看到开发者过度应用设计模式导致代码复杂化。一个好的经验法则是:当添加新功能时需要修改的代码越少,说明设计越合理。现代C++的特性让我们可以更灵活地应用设计模式,但核心的设计原则——高内聚、低耦合、单一职责等——永远不会过时。
