在现代C++开发中,设计模式不仅是代码风格的提升,更是解决复杂软件架构问题的关键工具。本文从单例、工厂、观察者和装饰器四种常用设计模式入手,探讨其在C++中的实现细节、使用场景以及可能的陷阱。
一、单例模式(Singleton)
单例模式保证一个类只有一个实例,并提供全局访问点。C++实现有多种方式,最常见的是懒汉式(Lazy)和饿汉式(Eager)两种变体。
// 饿汉式
class Logger {
public:
static Logger& instance() {
static Logger instance; // C++11保证线程安全
return instance;
}
void log(const std::string& msg) { std::cout << msg << std::endl; }
private:
Logger() = default;
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;
};
此实现利用函数内的静态局部对象,结合C++11的线程安全初始化特性,简洁且安全。若需更传统的实现,可使用双重检查锁(Double-Check Locking)结合 std::mutex,但要注意 `std::atomic
` 的使用以避免指令重排导致的初始化不完整。
二、工厂模式(Factory)
工厂模式通过抽象工厂接口,隐藏对象创建细节,提升系统的可扩展性。下面演示一个简单的形状工厂:
“`cpp
class Shape {
public:
virtual void draw() const = 0;
virtual ~Shape() = default;
};
class Circle : public Shape {
public:
void draw() const override { std::cout << "Circle\n"; }
};
class Square : public Shape {
public:
void draw() const override { std::cout << "Square\n"; }
};
class ShapeFactory {
public:
static std::unique_ptr
create(const std::string& type) {
if (type == “circle”) return std::make_unique
();
if (type == “square”) return std::make_unique
();
return nullptr;
}
};
“`
使用 `std::unique_ptr` 可以避免手动管理内存,并且通过工厂返回智能指针,使得资源释放更安全。若系统需要支持插件化或动态注册,进一步可以使用 `std::unordered_map<std::string, std::function<std::unique_ptr()>>` 来映射工厂函数。
三、观察者模式(Observer)
观察者模式用于解耦发出事件的对象和响应事件的对象。C++11的 `std::function` 与 `std::bind` 使得实现更简洁。
“`cpp
class Subject {
public:
using Observer = std::function;
void registerObserver(Observer obs) { observers_.push_back(obs); }
void notify(int value) {
for (auto& obs : observers_) obs(value);
}
private:
std::vector
observers_;
};
“`
若需要支持多线程环境,必须在 `notify` 与 `registerObserver` 之间加锁。C++20的 `std::atomic<std::vector>` 或读写锁(`std::shared_mutex`)可以提升并发性能。
四、装饰器模式(Decorator)
装饰器模式允许在不改变对象接口的前提下,动态地给对象添加功能。下面演示一个文本过滤装饰器的实现:
“`cpp
class Text {
public:
virtual std::string get() const = 0;
virtual ~Text() = default;
};
class PlainText : public Text {
public:
PlainText(const std::string& txt) : txt_(txt) {}
std::string get() const override { return txt_; }
private:
std::string txt_;
};
class TextDecorator : public Text {
public:
TextDecorator(std::unique_ptr
txt) : txt_(std::move(txt)) {}
std::string get() const override { return txt_->get(); }
protected:
std::unique_ptr
txt_;
};
class Capitalize : public TextDecorator {
public:
Capitalize(std::unique_ptr
txt) : TextDecorator(std::move(txt)) {}
std::string get() const override {
std::string s = TextDecorator::get();
std::transform(s.begin(), s.end(), s.begin(), ::toupper);
return s;
}
};
“`
使用 `std::unique_ptr` 让装饰器链的内存管理自动完成。装饰器在实际项目中常用于日志记录、缓存、权限校验等场景。
五、常见陷阱与最佳实践
1. **单例过度使用**
单例虽然提供全局访问,但会导致代码耦合与难以测试。建议只在真正需要全局唯一实例的场景下使用。
2. **工厂返回裸指针**
早期实现往往返回裸指针,易出现内存泄漏。现在更推荐返回智能指针,特别是 `std::unique_ptr` 或 `std::shared_ptr`。
3. **观察者生命周期管理**
观察者是函数对象,若观察者持有外部资源,应确保在 `Subject` 的生命周期结束前正确注销,否则可能导致悬空引用。
4. **装饰器深度堆栈**
多层装饰器会导致调用链深度增加,影响性能与调试。合理控制装饰器层数,或使用模板元编程在编译期合并装饰器。
5. **多线程安全**
任何共享资源的设计模式实现都必须考虑线程安全。C++20提供的 `std::atomic`、`std::shared_mutex` 与 `std::latch` 等工具,使得并发安全实现更加简洁。
结语
设计模式是C++软件工程中的重要工具。通过正确实现与使用,既能提高代码复用率,又能降低耦合度。本文仅覆盖了四种常用模式,读者可根据实际需求,进一步探索组合模式、代理模式、建造者模式等高级主题。祝你在C++编程道路上越走越远!</std::vector