在现代C++中,设计模式与语言特性相结合,能够让代码既具备高度可维护性,又不失性能与简洁性。本文以策略模式为例,演示如何利用C++11/14的特性(如std::function、std::unique_ptr、std::move、auto、范围for等)来实现一种既灵活又安全的策略体系,并结合泛型编程提升可复用性。
1. 策略模式概述
策略模式(Strategy)是一种行为型模式,核心思想是将算法封装在独立的类中,客户端可以在运行时根据需要动态切换策略。典型结构如下:
+-----------------+ +---------------------+
| Context | <----> | Strategy (interface)|
+-----------------+ +---------------------+
^ ^
| |
| |
+-----------------+ +---------------------+
| ConcreteStrategyA | | ConcreteStrategyB |
+-----------------+ +---------------------+
在C++中,传统实现往往使用纯虚基类和裸指针来管理策略对象。随着标准的演进,现代C++提供了更安全、更灵活的替代方案。
2. 使用 std::function 代替虚函数表
std::function 能够包装任意可调用对象(函数指针、成员函数、lambda、bind 对象等),并提供统一的调用接口。将策略接口改为:
#include <functional>
#include <string>
class Strategy {
public:
using Action = std::function<std::string(const std::string&)>;
// 注册策略
void set(Action func) { action_ = std::move(func); }
// 执行
std::string execute(const std::string& data) const {
if (!action_) throw std::runtime_error("Strategy not set");
return action_(data);
}
private:
Action action_;
};
优点:
- 无需继承:消除虚表开销。
- 灵活切换:运行时可以随意赋值不同的可调用对象。
- 类型安全:编译器会检查参数/返回值类型。
3. 结合 std::unique_ptr 实现策略生命周期管理
若某些策略需要动态创建且保持唯一所有权,可使用std::unique_ptr。例如:
class Context {
public:
explicit Context(std::unique_ptr <Strategy> strat)
: strategy_(std::move(strat)) {}
void setStrategy(std::unique_ptr <Strategy> strat) {
strategy_ = std::move(strat);
}
std::string process(const std::string& data) const {
return strategy_->execute(data);
}
private:
std::unique_ptr <Strategy> strategy_;
};
这样可以保证:
- 所有权明确:策略对象只属于
Context。 - 防止内存泄漏:
unique_ptr自动析构。
4. 示例:文本处理策略
下面给出一个完整示例,展示如何将三种不同文本处理策略(转大写、转小写、替换空格为下划线)组合使用。
#include <iostream>
#include <algorithm>
#include <cctype>
#include <memory>
#include <functional>
#include <string>
// Strategy 与 Context 代码如上
int main() {
// 创建三种策略
auto toUpper = std::make_unique <Strategy>();
toUpper->set([](const std::string& s){
std::string r = s;
std::transform(r.begin(), r.end(), r.begin(), [](unsigned char c){ return std::toupper(c); });
return r;
});
auto toLower = std::make_unique <Strategy>();
toLower->set([](const std::string& s){
std::string r = s;
std::transform(r.begin(), r.end(), r.begin(), [](unsigned char c){ return std::tolower(c); });
return r;
});
auto replaceSpace = std::make_unique <Strategy>();
replaceSpace->set([](const std::string& s){
std::string r = s;
std::replace(r.begin(), r.end(), ' ', '_');
return r;
});
// 使用 Context
Context ctx(std::move(toUpper));
std::string input = "Hello World! C++ 现代化";
std::cout << "Original: " << input << "\n";
std::cout << "Upper: " << ctx.process(input) << "\n";
ctx.setStrategy(std::move(toLower));
std::cout << "Lower: " << ctx.process(input) << "\n";
ctx.setStrategy(std::move(replaceSpace));
std::cout << "Replace: " << ctx.process(input) << "\n";
return 0;
}
输出:
Original: Hello World! C++ 现代化
Upper: HELLO WORLD! C++ 现代化
Lower: hello world! c++ 现代化
Replace: Hello_World!_C++_现代化
5. 进一步优化:使用模板参数化策略
如果策略不需要在运行时切换,且在编译期已知,可通过模板参数实现更高效的策略。示例:
template<typename Strategy>
class StaticContext {
public:
StaticContext() : strategy_() {}
std::string process(const std::string& data) const {
return strategy_.execute(data);
}
private:
Strategy strategy_;
};
使用时:
struct UpperCaseStrategy {
std::string execute(const std::string& s) const {
std::string r = s;
std::transform(r.begin(), r.end(), r.begin(), ::toupper);
return r;
}
};
StaticContext <UpperCaseStrategy> ctx;
std::cout << ctx.process("Hello") << "\n";
此时,编译器可以直接内联策略函数,消除运行时的函数调用开销。
6. 结语
通过现代C++11/14的函数对象、智能指针、移动语义与模板技术,策略模式的实现不再需要传统的继承与虚函数,既保持了设计模式的核心思想,又实现了更安全、更灵活、更高效的代码结构。建议在实际项目中,根据是否需要动态切换与编译期优化的权衡,选择适合的实现方式。