news 2026/10/28 19:02:27

C++11设计模式实战:从单例到CRTP的完整实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++11设计模式实战:从单例到CRTP的完整实现

一、引言

设计模式是面向对象设计中反复出现的问题的通用解决方案。C++ 作为一门支持多范式、强调零开销抽象的语言,设计模式在其中的落地方式与 Java、C# 有明显差异:C++ 有模板、RAII、值语义、移动语义、编译期多态等特性,很多模式可以用更轻量的方式实现,甚至可以在编译期完成。

本文按「创建型、结构型、行为型」三大类,挑选 C++ 工程中最常用、最有代表性的模式,逐一给出适用场景、结构说明和可编译运行的完整代码示例。所有示例均使用C++11 标准,可直接用g++ -std=c++11编译运行。

阅读建议:先理解每个模式要解决的「变化点」,再看代码。模式的价值在于隔离变化,而不是套用模板。

二、设计模式分类

分类典型模式C++11 中的常见落地方式
创建型单例、工厂方法、抽象工厂、建造者、原型静态局部变量、std::unique_ptr、模板工厂、拷贝构造
结构型适配器、装饰器、代理、组合、外观继承 + 组合、std::function、智能指针
行为型策略、观察者、命令、状态、模板方法虚函数、Lambda、std::function、CRTP

C++11 落地时需要特别注意三点:

  • 所有权清晰:用std::unique_ptr表达独占所有权,用std::shared_ptr表达共享所有权,避免裸new/delete。
  • 优先编译期多态:能用模板或 CRTP 在编译期解决的,就不必引入虚函数开销。
  • 遵循 RAII:资源获取即初始化,让析构函数负责释放,异常安全更容易保证。

C++11 带来的几个关键特性,直接改变了设计模式的写法:

  • override/final:让虚函数重写意图显式化,编译期即可发现签名错误。
  • = default/= delete:精确控制特殊成员函数,单例、Pimpl 都依赖它。
  • 右值引用与移动语义:让装饰器、代理等包装类可以零拷贝地转移内部对象。
  • Lambda 与std::function:让策略、观察者、命令模式可以摆脱继承体系。
  • Magic Static:函数内静态局部变量初始化线程安全,单例实现大幅简化。

三、创建型模式

3.1 单例模式(Singleton)

适用场景:全局唯一的配置管理器、日志器、连接池等。

模式特点:

  • 唯一性:全局只存在一个实例,所有调用方共享同一份状态。
  • 受控访问:构造函数私有,只能通过instance()获取对象,杜绝外部随意创建。
  • 延迟或提前创建:懒汉式按需创建,饿汉式在程序启动时创建,二者在启动开销与初始化顺序风险上各有取舍。
  • 线程安全依赖语言保证:C++11 的 Magic Static 让懒汉式无需手写双重检查锁。
  • 代价:本质是全局状态,容易隐藏依赖、影响可测试性,滥用会带来耦合。

C++11 起,函数内静态局部变量的初始化是线程安全的(Magic Static),这是实现懒汉式单例最简洁可靠的方式。

#include <iostream> #include <mutex> #include <string> class Logger { public: // C++11 起,静态局部变量初始化线程安全 static Logger& instance() { static Logger inst; return inst; } void log(const std::string& msg) { std::lock_guard<std::mutex> lock(mtx_); std::cout << "[LOG] " << msg << std::endl; } Logger(const Logger&) = delete; Logger& operator=(const Logger&) = delete; private: Logger() = default; std::mutex mtx_; }; int main() { Logger::instance().log("hello singleton"); return 0; }

要点:删除拷贝构造和拷贝赋值,防止被复制;用静态局部变量替代双重检查锁,代码更短且无内存序陷阱。

上面是懒汉式(第一次调用instance()时才创建实例)。与之相对的是饿汉式:程序启动阶段就完成实例创建,之后所有调用直接返回已有对象。饿汉式的优点是实现简单、无延迟、无并发初始化竞争;缺点是即使从未使用也会构造,且初始化顺序受静态对象初始化顺序影响。

#include <iostream> #include <mutex> #include <string> class ConfigManager { public: // 饿汉式:静态成员在 main 之前完成初始化 static ConfigManager& instance() { return inst_; } void set(const std::string& key, const std::string& value) { std::lock_guard<std::mutex> lock(mtx_); std::cout << "set " << key << " = " << value << std::endl; } ConfigManager(const ConfigManager&) = delete; ConfigManager& operator=(const ConfigManager&) = delete; private: ConfigManager() { std::cout << "ConfigManager constructed" << std::endl; } std::mutex mtx_; // 静态成员定义在类外,随程序启动构造 static ConfigManager inst_; }; // 定义并初始化静态成员(饿汉式的关键) ConfigManager ConfigManager::inst_; int main() { std::cout << "main start" << std::endl; ConfigManager::instance().set("mode", "release"); return 0; }

要点:静态成员inst_在main之前构造,因此输出顺序是「ConfigManager constructed」先于「main start」;同样要删除拷贝构造和拷贝赋值。若担心静态初始化顺序问题(多个翻译单元互相依赖),可改用函数内静态局部变量(即懒汉式)或Meyers Singleton的变体来规避。

对比项懒汉式(Magic Static)饿汉式(静态成员)
创建时机首次调用instance()程序启动、main之前
线程安全C++11 起由语言保证构造阶段单线程,天然安全
启动开销无无论是否使用都会构造
主要风险首次调用有轻微延迟静态初始化顺序问题

3.2 工厂方法模式(Factory Method)

模式特点:

  • 创建与使用分离:客户端只依赖抽象产品接口,不关心具体类型如何构造。
  • 符合开闭原则:新增产品只需新增对应的具体工厂,无需修改已有客户端代码。
  • 单一职责:把对象的创建逻辑集中到工厂中,产品类只关注自身行为。
  • 可扩展的创建族:一个工厂对应一类产品,便于按产品族组织代码。
  • 代价:每新增一种产品通常要新增一个工厂类,类数量会随产品种类增长。

适用场景:客户端只依赖抽象产品接口,具体类型由子类或工厂函数决定。

#include <iostream> #include <memory> #include <string> class Shape { public: virtual ~Shape() = default; virtual void draw() const = 0; }; class Circle : public Shape { public: void draw() const override { std::cout << "draw circle" << std::endl; } }; class Rectangle : public Shape { public: void draw() const override { std::cout << "draw rectangle" << std::endl; } }; class ShapeFactory { public: virtual ~ShapeFactory() = default; virtual std::unique_ptr<Shape> create() const = 0; }; class CircleFactory : public ShapeFactory { public: std::unique_ptr<Shape> create() const override { return std::unique_ptr<Shape>(new Circle()); } }; class RectangleFactory : public ShapeFactory { public: std::unique_ptr<Shape> create() const override { return std::unique_ptr<Shape>(new Rectangle()); } }; int main() { std::unique_ptr<ShapeFactory> factory(new CircleFactory()); std::unique_ptr<Shape> shape = factory->create(); shape->draw(); return 0; }

要点:工厂返回std::unique_ptr,所有权语义明确;C++11 中std::make_unique尚未进入标准库(C++14 才加入),因此这里使用std::unique_ptr<T>(new T())的写法。

3.3 简单工厂+注册表

模式特点:

  • 运行期解耦:类型选择由字符串或配置驱动,编译期不再依赖具体产品类型。
  • 注册即扩展:新增类型只需一行静态注册,工厂代码零改动。
  • 实现轻量:用std::function保存构造函数,避免为每个产品写一个工厂类。
  • 便于配置化:类型名可来自配置文件、命令行参数或插件清单。
  • 代价:类型名写错只能在运行期发现,需要额外的错误处理与注册顺序管理。

实际工程中更常见的是「字符串到构造函数的映射」,配合模板注册,新增类型无需修改工厂代码。

#include <functional> #include <iostream> #include <memory> #include <string> #include <unordered_map> class Shape { public: virtual ~Shape() = default; virtual void draw() const = 0; }; class Circle : public Shape
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/28 19:01:02

智能从来不是工具,是结构:Copilot 救不了组织

WGA 在 2026 年 10 月发布的一份组织设计报告里引过一项覆盖 5179 名客服的研究&#xff1a;AI 辅助让人均处理量涨了 14%&#xff0c;新手和低技能员工涨了 34%。WGA 据此判断&#xff0c;工作流、排队、升级路径与汇报线&#xff0c;四样一样没碰。 人更快了&#xff0c;组织…

作者头像 李华
网站建设 2026/10/28 19:00:42

OpenClaw 一键包|45.7MB 本地 AI 智能体全流程实操

&#x1f4d6; 前言 本文面向 Windows 系统用户&#xff0c;系统梳理 OpenClaw 的标准化部署流程。全程无需输入任何命令&#xff0c;所有操作均可通过可视化向导完成&#xff0c;即使是零基础用户&#xff0c;也能独立走完整套部署流程。文中同时汇总了高频报错对应的解决方案…

作者头像 李华
网站建设 2026/10/28 19:00:24

Linux 核心命令与符号 >> 权限管理体系专题

Linux 权限是安全管控的核心&#xff0c;文件读写执行权限、属主属组修改、文件特殊锁定属性、系统默认权限掩码&#xff0c;是运维排查权限问题高频知识点。一、chmodchange mode&#xff0c;修改文件 / 目录权限数字权限写法字符权限写法&#xff08; - &#xff09;-R&#…

作者头像 李华
网站建设 2026/10/28 18:59:15

产线设备选型的技术评估方法:从参数表到验收清单

设备选型这件事&#xff0c;多数团队的做法是"看参数表 比价格"。但参数表只能筛掉明显不合格的&#xff0c;真正决定成败的是验收清单。这篇讲一套可复用的技术评估方法。一、四步评估法第 1 步&#xff1a;定工况 — 扭矩区间、工件材质、节拍要求、环境条件。第 …

作者头像 李华
网站建设 2026/10/28 18:58:35

springboot易购数码商城系统11471-计算机课程设计、毕业设计

前言 ✨ 博主介绍&#xff1a;一线全栈工程师&#xff0c;毕设实战引路人。技术栈覆盖Java、Python、C#、PHP、Node.js及UniApp跨端开发&#xff0c;擅长多语言项目落地与架构设计。持续分享毕设源码、开题报告、技术选型心得与职场踩坑经验。用工程化思维写代码&#xff0c;帮…

作者头像 李华