Modern C++ 模板通用工厂
目录
Modern C++ 模板通用工厂
1 简单应用示例
1.1 示例代码
#include <iostream> #include <string> using namespace std; class Shape { public: virtual void calc_area() { cout << "call Shape::calc_area" << endl; } }; class Circle : public Shape { public: virtual void calc_area() { cout << "call Circle::calc_area" << endl; } }; class Square : public Shape { public: virtual void calc_area() { cout << "call Square::calc_area" << endl; } }; class Rectangle : public Shape { public: virtual void calc_area() { cout << "call Rectangle::calc_area" << endl; } }; int main() { Shape* pB = new Shape; pB->calc_area(); //call Shape::calc_area delete pB; Shape* pC = new Circle; pC->calc_area(); //call Circle::calc_area delete pC; Shape* pS = new Square; pS->calc_area(); //call Square::calc_area delete pS; Shape* pR = new Rectangle; pR->calc_area(); //call Rectangle::calc_area delete pR; system("pause"); }
1.2 分析说明
- 缺点:
- 每种类型对象在使用时候自己负责创建
- 子类多的话,代码很长
- 优点:运行时多态
2 简单工厂模式
2.1 示例代码
#include <iostream> #include <string> using namespace std; class Shape { public: virtual void calc_area() { cout << "call Shape::calc_area" << endl; } }; class Circle : public Shape { public: virtual void calc_area() { cout << "call Circle::calc_area" << endl; } }; class Square : public Shape { public: virtual void calc_area() { cout << "call Square::calc_area" << endl; } }; class Rectangle : public Shape { public: virtual void calc_area() { cout << "call Rectangle::calc_area" << endl; } }; class ShapeFactory { public: static Shape* createShape(std::string type) { if ("circle" == type) { return new Circle; } if ("square" == type) { return new Square; } if ("rectangle" == type) { return new Rectangle; } return nullptr; } }; int main() { Shape* ptr = ShapeFactory::createShape("circle"); if (ptr != nullptr) { ptr->calc_area(); //call Circle::calc_area delete ptr; } system("pause"); }
2.2 分析说明
- 缺点
- 每新增一种类型,都需要写个新类型的if-else(或switch-case)
- 优点
- 使用工厂统一创建每种类型的对象
3 工厂 + 静态注册
3.1 示例代码
#include <string> #include <iostream> #include <unordered_map> using namespace std; class Shape { public: virtual void calc_area() { cout << "call Shape::calc_area" << endl; } }; class Circle : public Shape { public: virtual void calc_area() { cout << "call Circle::calc_area" << endl; } }; class Square : public Shape { public: virtual void calc_area() { cout << "call Square::calc_area" << endl; } }; class Rectangle : public Shape { public: virtual void calc_area() { cout << "call Rectangle::calc_area" << endl; } }; typedef void* (*pCreateShape)(void); class CShapeFactory { public: ~CShapeFactory() = default; public: void* getShapeByName(const std::string& className) { auto iter = m_mapCShape.find(className); if (iter == m_mapCShape.end()) return nullptr; return iter->second(); } void registoryClass(const std::string& name, pCreateShape method) { m_mapCShape.insert(make_pair(name, method)); } static CShapeFactory& getInstance() { static CShapeFactory oFactory; return oFactory; } private: CShapeFactory() {}; std::unordered_map<std::string, pCreateShape> m_mapCShape; // 引入map }; // 注册动作类 class CRegisterAction { public: CRegisterAction(std::string className, pCreateShape pCreateFn) { CShapeFactory::getInstance().registoryClass(className, pCreateFn); } }; #define REGISTER_SERVICE(className) \ className* create##className() { \ return new className; \ } \ CRegisterAction createRegister##className( \ #className, (pCreateShape)create##className) REGISTER_SERVICE(Circle); REGISTER_SERVICE(Square); REGISTER_SERVICE(Rectangle); int main() { for (auto& item : { "Circle", "Square", "Rectangle" }) { auto pShape = (Shape*)CShapeFactory::getInstance().getShapeByName(item); if (pShape != nullptr) { pShape->calc_area(); delete pShape; } } system("pause"); } /* result: call Circle::calc_area call Square::calc_area call Rectangle::calc_area 请按任意键继续. . . */
3.2 分析说明
- 缺点
- 每个类都需要注册,容易遗漏
- 优点
- 优化掉了if-else和switch-case
4 CRTP工厂
4.1 示例代码
-
factory.hpp
#pragma once #include <memory> #include <functional> #include <unordered_map> #include <utility> // ref: http://www.nirfriedman.com/2018/04/29/unforgettable-factory/ namespace prefab { namespace patterns { template<typename I, typename Identify, typename... Args> class factory { friend I; using K = decltype(Identify::key); //typename Identify::key_type; using V = decltype(Identify::value);// typename Identify::value_type; struct key { key() {}; template<typename T> friend struct registar; }; factory() = default; using builder = std::function<std::unique_ptr<I>(Args...)>; using reader = V(*)(); static auto& builders() { static std::unordered_map<K, builder> container; return container; } static auto& readers() { static std::unordered_map<K, reader> container; return container; } public: using self = factory<I, Identify, Args...>; template<typename... Ts> static std::unique_ptr<I> make(K const& k, Ts&&... args) { auto it = builders().find(k); if (it == builders().end()) return nullptr; return it->second(std::forward<Ts>(args)...); } static std::pair<bool, V> identify(K const& k) { std::pair<bool, V> result; result.first = false; auto it = readers().find(k); if (it == readers().end()) return result; result.first = true; result.second = it->second(); return result; } template<typename T> struct registar : I { friend T; static bool register_() { const auto r = T::identify().key; factory::builders()[r] = [](Args&&... args)->std::unique_ptr<I> { return std::make_unique<T>(std::forward<Args>(args)...); }; factory::readers()[r] = []()->V { return T::identify().value; }; return true; } static bool registered; private: registar() : I(key{}) { (void)registered; } }; }; template<typename I, typename Identify, typename... Args> template<typename T> bool factory<I, Identify, Args...>::registar<T>::registered = factory<I, Identify, Args...>::registar<T>::register_(); } } #if 0 #include <iostream> #include <string> namespace prefab_patterns_example { template<typename I, typename Identify, typename... Args> using Factory = prefab::patterns::factory<I, Identify, Args...>; struct task_identify { std::string key; std::string value; }; struct task :public Factory<task, task_identify, int> { task(self::key) {}; virtual void execute() = 0; }; struct print :task::registar<print> { print(int) {}; void execute()override { std::cout << "print\n"; }; static task_identify identify() noexcept { (void)registered; // MSVC has bug,use this force register return task_identify{ "print","task_print" }; } }; } #endif -
CShapeFactory.hpp
#include <string> #include <iostream> #include "factory.hpp" template<typename I, typename Identify, typename... Args> using Factory = prefab::patterns::factory<I, Identify, Args...>; struct IShape_identify { std::string key; std::string value; }; struct IShape : public Factory<IShape, IShape_identify> { IShape(self::key) {}; virtual void calc_area() = 0; }; struct Circle : public IShape::registar<Circle> { public: Circle() {} virtual void calc_area() { std::cout << "call Circle::calc_area" << std::endl; } static IShape_identify identify() noexcept { return IShape_identify{ "C", "Circle" }; } }; struct Square : public IShape::registar<Square> { public: Square() {} virtual void calc_area() { std::cout << "call Square::calc_area" << std::endl; } static IShape_identify identify() noexcept { return IShape_identify{ "S", "Square" }; } }; struct Rectangle : public IShape::registar<Rectangle> { public: Rectangle() {} virtual void calc_area() { std::cout << "call Rectangle::calc_area" << std::endl; } static IShape_identify identify() noexcept { return IShape_identify{ "R", "Rectangle" }; } }; -
main.cpp
#include "CShapeFactory.hpp" int main() { for (auto& item : { "C", "S", "R" }) { auto target = IShape::make(item); if (target != nullptr) { target->calc_area(); } } system("pause"); } /* result: call Circle::calc_area call Square::calc_area call Rectangle::calc_area 请按任意键继续. . . */
4.2 分析说明
关于CRTP,详见随笔《C++ CRTP》
4.3 过程中问题
-
编译错误:C7510 “registar”: 模板从属名称的使用必须以“模板”为前缀
解决方案:本地VS2019,关闭工程的“符合模式”即可(默认开启),如下图:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
2020-01-07 Cmder安装
2019-01-07 linux C access
2013-01-07 QTableWidget控件总结
2013-01-07 冒泡排序