C++工厂模式与在反射中的应用

C++工厂模式

设计模式:可复用面向对象软件及基础:2-3 创建者模式:(Factory method)工厂方法_~怎么回事啊~的博客-CSDN博客

C++ 反射


反射的概念:

        指程序在运行时,访问、检测和修改它本身状态或行为的一种能力。

        简单的来说,就是一种自描述和自控制的能力。如果联想到镜子,就可以很好的理解,你能通过镜子看到自己,包括自己的动作,自己的外表。唯一不同的地方是,计算机语言的反射能力还包含对看到的自己采取措施。

反射的作用

在计算机编程语言中,反射机制可以用来:

  1. 获取类型的信息,包括属性、方法
  2. 动态调用方法
  3. 动态构造对象
  4. 从程序集中获得类型

反射的缺点

性能:反射可以理解成是一种解释操作,这个过程总是要慢于直接调用的。当然,性能问题的程度是可以控制的,如果程序在很少涉及的地方使用,性能将不会是一个问题。
反射模糊了程序内部实际发生的事情,会比直接代码更加复杂。
缺点不能掩饰其优点,针对不同的场景使用合理的技术才是最高境界。

反射的使用场景

  1. 序列化(Serialization)和数据绑定(Data Binding)
  2. 远程方法调用(RMI)
  3. 对象/关系数据映射(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 + 想调用的类名字符串 来获取实际的类

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值