C++工厂模式
设计模式:可复用面向对象软件及基础:2-3 创建者模式:(Factory method)工厂方法_~怎么回事啊~的博客-CSDN博客
C++ 反射
反射的概念:
指程序在运行时,访问、检测和修改它本身状态或行为的一种能力。
简单的来说,就是一种自描述和自控制的能力。如果联想到镜子,就可以很好的理解,你能通过镜子看到自己,包括自己的动作,自己的外表。唯一不同的地方是,计算机语言的反射能力还包含对看到的自己采取措施。
反射的作用
在计算机编程语言中,反射机制可以用来:
- 获取类型的信息,包括属性、方法
- 动态调用方法
- 动态构造对象
- 从程序集中获得类型
反射的缺点
性能:反射可以理解成是一种解释操作,这个过程总是要慢于直接调用的。当然,性能问题的程度是可以控制的,如果程序在很少涉及的地方使用,性能将不会是一个问题。
反射模糊了程序内部实际发生的事情,会比直接代码更加复杂。
缺点不能掩饰其优点,针对不同的场景使用合理的技术才是最高境界。
反射的使用场景
- 序列化(Serialization)和数据绑定(Data Binding)
- 远程方法调用(RMI)
- 对象/关系数据映射(O/R mapping)
关于c++的反射
C++ 原生是不支持反射的,需要自己造轮子。
何在C++中实现简单的反射。 场景:
C++序列化,与反序列化。序列化就是将对象编程二进制的形式存储在磁盘上,或者通过网络传输给另一台机器。反序列化就是序列化的逆过程。但是这个逆过程,必须要根据字符串来判断将二进制流转化成什么类型的对象。
工厂模式,常常是根据一个字符串来获取想要的对象。但是为了满足开闭原则,我们不能简单的在工厂类中不断的修改生产函数来扩展不同的类型。这个时候,需要利用反射,使用抽象类。
思路是:
- 使用map,映射字符串和生产函数
- 每次构造新类型时,将生产函数注册到map中
- 工厂函数通过map获得生产函数,建造不同的对象
工厂对象
#pragma once
#include <map>
#include <string>
/// 对象创建模版函数, 用到了 c++11 的可变参数
template<class YourClass, typename ...ArgType>
void* __createObjFunc(ArgType... arg)
{
return new YourClass(arg...);
}
/// 需要反射的类使用该宏注册
#ifndef ReflectRegister
#define ReflectRegister(YourClass, ...) \
static int __type##YourClass = ObjFactory::regCreateObjFunc(#YourClass, (void*)&__createObjFunc<YourClass, ##__VA_ARGS__>);
#endif
/// 对象工厂,根据类名创建
class ObjFactory
{
public:
/// 根据类名创建 BaseClass 子类的对象
template<class BaseClass, typename ...ArgType>
static BaseClass* createObj(const char* className, ArgType... arg)
{
typedef BaseClass*(*_CreateFactory)(ArgType...);
auto& _funcMap = _GetStaticFuncMap();
auto iFind = _funcMap.find(className);
if (iFind == _funcMap.end())
return nullptr;
else
return reinterpret_cast<_CreateFactory>(_funcMap[className])(arg...);
}
/// 注册“创建类的对象的工厂函数”
static int regCreateObjFunc(const char* className, void* func)
{
_GetStaticFuncMap()[className] = func;
return 0;
}
private:
/// 获取 函数名==>函数指针 的映射。
static std::map<const char*, void*>& _GetStaticFuncMap()
{
static std::map<const char*, void*> _classMap;
return _classMap;
}
};
在工厂对象中,通过一个map:static std::map<const char*, void*> _classMap;来映射<类名,构造函数>
通过对外暴露接口:static BaseClass* createObj(const char* className, ArgType... arg) 来支持通过字符串className来获取对象。
对于需要支持反射操作的类,需要通过一个宏定义来注册:ReflectRegister(YourClass, ...)。
关于宏的用法,复习一下:
#define N 2 + 2 // 仅仅是字符串替换
#define N (2 + 2) // 也是字符串 ,但是是(2 + 2)
#define area(x) (x) * (x) // 带参的宏定义参会当作字符串直接替换
三种特殊的符号:
# #define Conn(x, y) x##y // 表示连接,数字,字符串都可以
## #define ToString(x) #x // 就是加上双引号
#@ #define ToChar(x) #@x //就是加上单引号, 越界会报错
具体使用:假设有一个基类BaseShape,两个派生类 Rectangle、Triangle ,重写draw接口,代码如下:
BaseShape.h
#pragma once
class BaseShape
{
public:
virtual void draw() = 0;
};
Rectangle.h
#pragma once
#include <iostream>
#include "BaseShape.h"
#include "ObjFactory.h"
class Triangle : public BaseShape
{
std::string m_userData;
public:
Triangle(std::string userData)
: m_userData(userData)
{}
void draw() override
{
std::cout << "I'm Triangle," << m_userData.c_str();
}
};
ReflectRegister(Triangle, std::string) // 注册三角形类
Triangle.h
#pragma once
#include <iostream>
#include "BaseShape.h"
#include "ObjFactory.h"
class Triangle : public BaseShape
{
std::string m_userData;
public:
Triangle(std::string userData)
: m_userData(userData)
{}
void draw() override
{
std::cout << "I'm Triangle," << m_userData.c_str();
}
};
ReflectRegister(Triangle, std::string) // 注册三角形类
在派生类中,通过宏定义ReflectRegister完成注册
使用:
#include<iostream>
#include "BaseShape.h"
#include "ObjFactory.h"
int main() {
BaseShape* pBase;
pBase = ObjFactory::createObj<BaseShape>("Rectangle");
if (pBase != nullptr)
pBase->draw();
pBase = ObjFactory::createObj<BaseShape, std::string>("Triangle", "hello");
if (pBase != nullptr)
pBase->draw();
return 0;
}
在实际使用的代码中,只需要调用 ObjFactory::createObj + 想调用的类名字符串 来获取实际的类