{"id":18656283,"url":"https://github.com/downdemo/design-patterns-in-cpp17","last_synced_at":"2025-04-09T09:08:59.808Z","repository":{"id":37629632,"uuid":"226845832","full_name":"downdemo/Design-Patterns-in-Cpp17","owner":"downdemo","description":"C++17 implementation of 23 GoF design patterns for zero memory leaks using smart pointers.","archived":false,"fork":false,"pushed_at":"2022-10-03T08:54:38.000Z","size":36,"stargazers_count":307,"open_issues_count":0,"forks_count":55,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-08-03T15:05:15.720Z","etag":null,"topics":["design-patterns","smart-pointers"],"latest_commit_sha":null,"homepage":"https://downdemo.github.io/Design-Patterns-in-Cpp17/","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/downdemo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-12-09T10:33:16.000Z","updated_at":"2024-07-30T02:01:58.000Z","dependencies_parsed_at":"2022-07-12T16:34:33.928Z","dependency_job_id":null,"html_url":"https://github.com/downdemo/Design-Patterns-in-Cpp17","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/downdemo%2FDesign-Patterns-in-Cpp17","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/downdemo%2FDesign-Patterns-in-Cpp17/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/downdemo%2FDesign-Patterns-in-Cpp17/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/downdemo%2FDesign-Patterns-in-Cpp17/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/downdemo","download_url":"https://codeload.github.com/downdemo/Design-Patterns-in-Cpp17/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248008630,"owners_count":21032556,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["design-patterns","smart-pointers"],"created_at":"2024-11-07T07:22:47.564Z","updated_at":"2025-04-09T09:08:59.791Z","avatar_url":"https://github.com/downdemo.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Supported Platforms](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20Mac%20OS-green.svg)\n[![Build Status](https://travis-ci.org/downdemo/Design-Patterns-in-Cpp17.svg?branch=master)](https://travis-ci.org/downdemo/Design-Patterns-in-Cpp17)\n[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/LICENSE)\n\n## Build\n\n* [C++17 is required](https://en.cppreference.com/w/cpp/17), Windows, Mac OS and Linux are supported, build by [CMake](https://cmake.org/download/)\n\n```\ngit clone git@github.com:downdemo/Design-Patterns-in-Cpp17.git\ncd Design-Patterns-in-Cpp17\ncmake . -Bbuild\ncd build\ncmake --build .\n```\n\n## 设计模式简介\n\n* 设计模式的概念最初源自建筑行业，建筑师 Christopher Alexander 曾这样解释过模式的概念：“总会有一类问题在我们身边反复出现，模式就是针对这一类问题的通用解法。当问题反复出现时，直接套用这个解法即可，而不需要去重新解决问题。”\n\n\u003e Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice.\n\n* 后来，模式的思想也被引入了软件工程领域，[软件工程](https://book.douban.com/subject/6047742/)中提出了以下设计原则，设计模式也遵循了这些原则\n  * 开闭原则（The Open-Closed Principle，OCP）：对扩展开放，对修改关闭。修改程序时，不需要修改类内部代码就可以扩展类的功能，如[装饰器模式](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/src/decorator.cpp)\n  * Liskov 替换原则（Liskov Substitution Principle，LSP）：任何基类出现的地方，都可以用派生类替换\n  * 依赖倒置原则（Dependency Inversion Principle，DIP）：针对接口（纯虚函数）编程，而非针对实现编程\n  * 接口分离原则（Interface Segregation Principle，ISP）：接口功能的粒度应该尽可能小，多个功能分离的小接口、单个合并了这些功能的大接口，前者是更好的做法，因为这样降低了依赖，耦合度更低\n  * 发布复用等价性原则（Release Reuse Equivalency Principle，REP）：复用的粒度就是发布的粒度。第三方库的作者需要维护每个版本，作者可以修改代码，但是用户不需要了解源码的变化，而可以自由选择使用哪个版本的库，因此库作者应该将可复用的类打包成包，以包为单位来更新，而不是更新每个类，比如 [Boost](https://www.boost.org/) 有一个版本号，而其中的每个子部分（如 [Boost.Asio](https://www.boost.org/doc/libs/1_79_0/doc/html/boost_asio.html)）又有各自独立的版本号\n  * 共同封装原则（Common Closure Principle，CCP）：一同变更的类应该合在一起，如果一些类处理相同的功能或行为域，那么这些类应该根据内聚性分到一组打包，这样需要修改某个域的功能时，只需要修改这个包中的代码\n  * 共同复用原则（Common Reuse Principle，CRP）：不能一起被复用的类不能被分到一组。当包中的类变化时，包的版本号也会变化，如果不相关的类被分到一组，就会导致本来无必要的包的版本升级，为此又需要进行本来无必要的集成和测试\n* 设计模式是从已有的软件设计中，针对重复出现的问题提取出的一套经验论，其概念源自 [*Design Patterns: Elements of Reusable Object-Oriented Software*](https://www.oreilly.com/library/view/design-patterns-elements/0201633612/)，此书由四人合著，因此简称 GoF（Gang of Four）。GoF 总结归纳了 23 种设计模式，分为创建型（Creational）、结构型（Structural）、行为型（Behavioral）三类。设计模式的思想在编程世界中很常见，如 [C#](https://docs.microsoft.com/en-us/dotnet/csharp/) 的委托与事件、[Qt](https://doc.qt.io/) 的信号槽、[RxJS](https://github.com/ReactiveX/rxjs) 的响应式编程本质都是[观察者模式](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/src/observer.cpp)\n\n|创建型模式|中文名|说明|实现|\n|:-|:-|:-|:-|\n|Abstract Factory/Kit|抽象工厂模式|[README](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/docs/creational_pattern/abstract_factory.md)|[C++](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/src/abstract_factory.cpp)|\n|Builder|建造者模式|[README](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/docs/creational_pattern/builder.md)|[C++](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/src/builder.cpp)|\n|Factory Method/Virutal Contructor|工厂方法模式|[README](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/docs/creational_pattern/factory_method.md)|[C++](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/src/factory_method.cpp)|\n|Prototype|原型模式|[README](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/docs/creational_pattern/prototype.md)|[C++](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/src/prototype.cpp)|\n|Singleton|单例模式|[README](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/docs/creational_pattern/singleton.md)|[C++](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/src/singleton.cpp)|\n\n|结构型模式|中文名|说明|实现|\n|:-|:-|:-|:-|\n|Adapter/Wrapper|适配器模式|[README](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/docs/structural_pattern/adapter.md)|[C++](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/src/adapter.cpp)|\n|Bridge/Handle/Body|桥接模式|[README](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/docs/structural_pattern/bridge.md)|[C++](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/src/bridge.cpp)|\n|Composite|组合模式|[README](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/docs/structural_pattern/composite.md)|[C++](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/src/composite.cpp)|\n|Decorator/Wrapper|装饰器模式|[README](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/docs/structural_pattern/decorator.md)|[C++](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/src/decorator.cpp)|\n|Facade|外观模式|[README](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/docs/structural_pattern/facade.md)|[C++](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/src/facade.cpp)|\n|Flyweight|享元模式|[README](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/docs/structural_pattern/flyweight.md)|[C++](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/src/flyweight.cpp)|\n|Proxy/Surrogate|代理模式|[README](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/docs/structural_pattern/proxy.md)|[C++](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/src/proxy.cpp)|\n\n|行为型模式|中文名|说明|实现|\n|:-|:-|:-|:-|\n|Chain of Responsibility|责任链模式|[README](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/docs/behavioral_pattern/chain_of_responsibility.md)|[C++](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/src/chain_of_responsibility.cpp)|\n|Command/Action/Transaction|命令模式|[README](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/docs/behavioral_pattern/command.md)|[C++](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/src/command.cpp)|\n|Interpreter|解释器模式|[README](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/docs/behavioral_pattern/interpreter.md)|[C++](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/src/interpreter.cpp)|\n|Iterator/Cursor|迭代器模式|[README](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/docs/behavioral_pattern/iterator.md)|[C++](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/src/iterator.cpp)|\n|Mediator|中介者模式|[README](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/docs/behavioral_pattern/mediator.md)|[C++](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/src/mediator.cpp)|\n|Memento/Token|备忘录模式|[README](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/docs/behavioral_pattern/memento.md)|[C++](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/src/memento.cpp)|\n|Observer/Dependents/Publish-Subscribe|观察者模式|[README](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/docs/behavioral_pattern/observer.md)|[C++](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/src/observer.cpp)|\n|State/Objects for States|状态模式|[README](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/docs/behavioral_pattern/state.md)|[C++](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/src/state.cpp)|\n|Strategy/Policy|策略模式|[README](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/docs/behavioral_pattern/strategy.md)|[C++](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/src/strategy.cpp)|\n|Template Method|模板方法模式|[README](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/docs/behavioral_pattern/template_method.md)|[C++](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/src/template_method.cpp)|\n|Visitor|访问者模式|[README](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/docs/behavioral_pattern/visitor.md)|[C++](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/src/visitor.cpp)|\n\n## C++ 中的设计模式\n\n* 设计模式并非十全十美，一些模式本质就意味着高耦合（如[观察者模式](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/src/observer.cpp)），用 C++ 实现时要注意正确管理资源生命周期，避免内存泄漏\n* C++11 引入了 [std::shared_ptr](https://en.cppreference.com/w/cpp/memory/shared_ptr)、[std::unique_ptr](https://en.cppreference.com/w/cpp/memory/unique_ptr)、[std::weak_ptr](https://en.cppreference.com/w/cpp/memory/weak_ptr) 三种智能指针来解决原始指针生命周期管理困难的痛点，此项目通过使用它们来避免内存泄漏，以深入理解三者的使用场景及其区别。需要注意的是，实际工程可能需要考虑更多问题（如线程安全、智能指针的额外开销、代码可读性），应当避免滥用智能指针（同理也应当避免滥用设计模式）\n* 使用智能指针并不意味着一定没有内存泄漏，比如循环引用的情况（实现[观察者模式](https://github.com/downdemo/Design-Patterns-in-Cpp17/blob/master/src/observer.cpp)时容易写出类似代码）\n\n```cpp\nclass B;\n\nclass A {\n public:\n  std::shared_ptr\u003cB\u003e b;\n  ~A() { std::cout \u003c\u003c \"Destroy A\\n\"; }\n};\n\nclass B {\n public:\n  std::shared_ptr\u003cA\u003e a;\n  ~B() { std::cout \u003c\u003c \"Destroy B\\n\"; }\n};\n\nint main() {\n  {\n    auto p = std::make_shared\u003cA\u003e();\n    p-\u003eb = std::make_shared\u003cB\u003e();\n    p-\u003eb-\u003ea = p;\n    assert(p.use_count() == 2);\n  }  // p 的引用计数由 2 减为 1，不会析构\n     // 于是 p-\u003eb 也不会析构，导致两次内存泄漏\n}\n```\n\n* 分析 [std::shared_ptr](https://en.cppreference.com/w/cpp/memory/shared_ptr) 的生命周期，适当使用 [std::weak_ptr](https://en.cppreference.com/w/cpp/memory/weak_ptr) 即可避免此类情况\n\n```cpp\nclass B;\n\nclass A {\n public:\n  std::shared_ptr\u003cB\u003e b;\n  ~A() { std::cout \u003c\u003c \"Destroy A\\n\"; }\n};\n\nclass B {\n public:\n  std::weak_ptr\u003cA\u003e a;\n  ~B() { std::cout \u003c\u003c \"Destroy B\\n\"; }\n};\n\nint main() {\n  {\n    auto p = std::make_shared\u003cA\u003e();\n    p-\u003eb = std::make_shared\u003cB\u003e();\n    p-\u003eb-\u003ea = p;  // weak_ptr 不增加 shared_ptr 的引用计数\n    assert(p.use_count() == 1);\n  }  // Destroy A\n     // Destroy B\n}\n```\n\n### 检测内存泄漏的方法\n\n* 开发环境使用 [Visual Studio](https://visualstudio.microsoft.com/zh-hans/?rr=https%3A%2F%2Fcn.bing.com%2F)\n* 在程序结束点调用 [_CrtDumpMemoryLeaks()](https://docs.microsoft.com/zh-cn/previous-versions/d41t22sb(v=vs.120)?redirectedfrom=MSDN)\n\n```cpp\n#include \u003ccrtdbg.h\u003e\n\n#ifdef _DEBUG\n#define new new (_NORMAL_BLOCK, __FILE__, __LINE__)\n#endif\n\nint main() {\n  int* p = new int(42);\n  _CrtDumpMemoryLeaks();\n}\n```\n\n* 调试时将提示第 9 行产生 4 字节的内存泄漏\n\n```\nDetected memory leaks!\nDumping objects -\u003e\nxxx.cpp(9) : {88} normal block at 0x008C92D0, 4 bytes long.\n Data: \u003c*   \u003e 2A 00 00 00 \nObject dump complete.\n```\n\n* 这种方法的原理是，在执行此函数时，检查所有未回收的内存，因此存在析构函数还未执行而误报的情况\n\n```cpp\n#include \u003ccrtdbg.h\u003e\n\n#include \u003cmemory\u003e\n\n#ifdef _DEBUG\n#define new new (_NORMAL_BLOCK, __FILE__, __LINE__)\n#endif\n\nint main() {\n  auto p = std::make_shared\u003cint\u003e(42);\n  _CrtDumpMemoryLeaks();  // 此时 std::shared_ptr 还未析构，因此报告内存泄漏\n}\n```\n\n* 更好的方法是在开始点使用 [_CrtSetDbgFlag()](https://docs.microsoft.com/zh-cn/previous-versions/5at7yxcs(v=vs.120)?redirectedfrom=MSDN)，它在程序退出时调用 [_CrtDumpMemoryLeaks()](https://docs.microsoft.com/zh-cn/previous-versions/d41t22sb(v=vs.120)?redirectedfrom=MSDN)\n\n```cpp\n#include \u003ccrtdbg.h\u003e\n\n#ifdef _DEBUG\n#define new new (_NORMAL_BLOCK, __FILE__, __LINE__)\n#endif\n\nint main() {\n  _CrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG));\n  int* p = new int(42);\n}\n```\n\n* 该项目中的源码使用上述方式检测均无内存泄漏\n* 为了尽可能简单，所有代码均以单个源文件的形式实现\n* 源码中未使用平台相关 API，因此在任何支持 C++17 标准的平台均可通过编译\n\n### 智能指针的传参方式选择\n\n* 以下两种传参方式，应该如何选择？\n\n```cpp\nvoid f(std::shared_ptr\u003cA\u003e p);         // 按值传递\nvoid f(const std::shared_ptr\u003cA\u003e\u0026 p);  // 按 const 引用传递\n```\n\n* 不同的传参方式表达了不同的语义，通常情况下，按 const 引用传递是最稳妥且没有心智负担的\n\n```cpp\nvoid f(A\u0026);  // 仅使用对象，不涉及对象资源所有权的管理\nvoid f(A*);  // 仅使用对象，不涉及对象资源所有权的管理\n\nvoid f(std::unique_ptr\u003cA\u003e);  // 用于转移唯一所有权（用 std::move 传入）\nvoid f(std::unique_ptr\u003cA\u003e\u0026);        // 用于重置内部对象\nvoid f(const std::unique_ptr\u003cA\u003e\u0026);  // 不如直接传引用或原始指针\n\nvoid f(std::shared_ptr\u003cA\u003e);   // 引用计数共享\n                              // 可用 std::move 传入 std::unique_ptr 实参\nvoid f(std::shared_ptr\u003cA\u003e\u0026);  // 引用计数不变，用于重置内部对象\n                              // 不可接受 std::unique_ptr 实参\nvoid f(const std::shared_ptr\u003cA\u003e\u0026);  // 引用计数不变，不可重置内部对象\n                                    // 可用 std::move 传入 std::unique_ptr 实参\n```\n\n* 对于按值传递，只在之后一定会对其拷贝的场景才可能考虑使用\n\n```cpp\nvoid f(const std::shared_ptr\u003cA\u003e\u0026 p) {\n  auto q = p;  // 拷贝 p\n  // ...\n}\n```\n* 如果按值传递，拷贝可以改用移动，比起传引用后使用拷贝，只多出一次移动操作，如果移动开销很小，这种做法简化了代码，是一个很好的选择\n\n```cpp\nvoid f(std::shared_ptr\u003cA\u003e p) {  // 拷贝一次\n  auto q = std::move(p);        // 移动一次\n  // ...\n}\n```\n\n* 这种情况常见于构造函数中\n\n```cpp\nclass X {\n public:\n  explicit X(std::shared_ptr\u003cA\u003e p) : p_(std::move(p)) {}\n\n private:\n  std::shared_ptr\u003cA\u003e p_;\n};\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdowndemo%2Fdesign-patterns-in-cpp17","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdowndemo%2Fdesign-patterns-in-cpp17","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdowndemo%2Fdesign-patterns-in-cpp17/lists"}