C++ 反射机制详解及实例代码

C++ 反射机制

一.前言:

Java有着一个非常突出的动态相关机制:Reflection,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。然而C++是不支持反射机制,虽然C++有RTTI(运行时类型识别)。但是想要实现C++对象序列化,序列化就是存储到磁盘上,将对象变成一定格式的二进制编码,然后要用的时候再将保存在磁盘上的二进制编码转化成一个内存中的对象,这个过程中总是需要有一个指示来告诉编译器要生成什么样的对象,最简单的方式当然就是类名了,例如:将一个ClassXXX对象存储到磁盘上,再从磁盘读取的时候让编译器根据“ClassXXX”名称来new一个对象。

ClassT* obj = FactoryCreate("ClassT"); 

类似于以上的语法,虽然C++没有自带的语法可以实现,但是我们可以自己通过其他方法来实现。(由于本人能力有限,所以该篇博客只是讲解如何简单的实现这个反射机制,而对C++中拥有这个反射机制是否有必要不做任何讨论。当然,如果博客中有什么地方说的有错误还望大家可以在下面评论指出谢谢)

二.实现:

1.我们很容易可以想到可以使用简单工厂模式来实现这个效果:比如

class Object
{
public:
  virtual string ToString() = 0;
};

这个是所有需要实现反射机制的类需要继承的基类,然后派生出来的类只需要再实现这个ToString即可。例如:

class MyClass :public Object
{
public:
  virtual string ToString(){ return "MyClass"; }
};

然后就是用于产生对象的工厂。

Object* FactoryCreat(const string& className)
{
  if (className == "ClassA")
    return new ClassA;
  else if (className == "ClassB")
    return new ClassB;
  else if(className == "ClassC")
    return new ClassC;
  else if(className == "ClassD")
    return new ClassD;
  else if(className == "ClassE")
    return new ClassE;
  ...
}

我们使用就可以这样:

int main()
{
  Object* obj = FactoryCreat("MyClass");
  cout << obj->ToString();
  delete obj;
  return 0;
}

我们使用简单工厂模式感觉好像是解决了问题,可以实现用字符串去new一个对应的对象,但是假如我们要新建一个类或者修改一个类,那么这个FactoryCreat都要进行修改。十分不利于维护。所以我们需要换一个方式来处理。

2.工厂模式结合回调机制。

首先我们要梳理一下这个方法的基本脉络:

1.工厂内部需要有个映射,也就是一个字符串对应一个类new的方法。
2.工厂给出一个接口,我们传入字符串,那么返回这个字符串对应的方法new出来的对象指针。
3.我们新建的类,如果需要支持反射机制,那么这个类需要自动将自己的new方法和名字注册到工厂的映射中。

OK,如果我们能完成以上几个要求,那么我们在类进行拓展的时候需要改动的地方就十分少了。对于工厂的代码我们基本上是不会改变的。也就基本上实现了我们C++反射机制的基本功能。

下面我们来一步一步解析代码:

首先我们还是需要一个Object作为需要支持反射机制类的基类

//Reflex.h
class Object
{
public:
  Object(){}
  virtual ~Object(){}
  static bool Register(ClassInfo* ci);     //注册传入一个classInfo(类信息),将这个类的信息注册到映射中
  static Object* CreateObject(string name);   //工厂生产对象的接口
}; 

然后是实现:

//Reflex.cpp
static std::map< string, ClassInfo*> *classInfoMap = NULL;
bool Object::Register(ClassInfo* ci)
{
  if (!classInfoMap)  {
    classInfoMap = new std::map< string, ClassInfo*>();   //这里我们是通过map来存储这个映射的。
  }
  if (ci) {
    if (classInfoMap->find(ci->m_className) == classInfoMap->end()){
      classInfoMap->insert(std::map< string, ClassInfo*>::value_type(ci->m_className, ci)); // 类名 <-> classInfo
    }
  }
  return true;
}
Object* Object::CreateObject(std::string name)
{
  std::map< string, ClassInfo*>::const_iterator iter = classInfoMap->find(name);
  if (classInfoMap->end() != iter) {
    return iter->second->CreateObject();     //当传入字符串name后,通过name找到info,然后调用对应的CreatObject()即可
  }
  return NULL;
}

剩下的我们还需要一个classinfo类就大功告成了:

//Reflex.h 

typedef Object* (*ObjectConstructorFn)(void);
class ClassInfo
{
public:
  ClassInfo(const std::string className, ObjectConstructorFn ctor)
    :m_className(className), m_objectConstructor(ctor)
  {
    Object::Register(this);       //classInfo的构造函数是传入类名和类对应的new函数然后自动注册进map中。
  }
  virtual ~ClassInfo(){}
  Object* CreateObject()const { return m_objectConstructor ? (*m_objectConstructor)() : 0; }
  bool IsDynamic()const { return NULL != m_objectConstructor; }
  const std::string GetClassName()const { return m_className; }
  ObjectConstructorFn GetConstructor()const{ return m_objectConstructor; }
public:
  string m_className;
  ObjectConstructorFn m_objectConstructor;
};

有了这些类后,我们只需要让需要支持反射的类满足以下要求即可:

1.继承Object类。
2.重载一个CreatObject()函数,里面 return  new 自身类。
3.拥有一个classInfo的成员并且用类名和CreatObject初始化。

满足以上三个要求的类我们就可以利用反射机制来创建对象了。我们可以看下面的例子:

class B : public Object
{
public:
  B(){ cout << hex << (long)this << " B constructor!" << endl; }
  ~B(){ cout << hex << (long)this << " B destructor!" << endl; }
  virtual ClassInfo* GetClassInfo() const{ return &ms_classinfo; }
  static Object* CreateObject() { return new B; }
protected:
  static ClassInfo ms_classinfo;
};
ClassInfo B::ms_classinfo("B", B::CreateObject);

使用的话我们就只需要调用Object::CreatObject(string) 传入类名即可。

int main()
{
  Object* obj = Object::CreateObject("B");
  delete obj;
  return 0;
}

基本上反射机制的功能就实现了,而且使用回调注册在后期拓展上也容易维护。

三.使用宏简化代码:

其实大家发现,因为我们要让类支持反射那么就要满足我们上面的那三个要求,但是每个类都要写这样相似的东西。仔细一看,包括函数申da's明、函数定义、函数注册,每个类的代码除了类名外其它都是一模一样的,有没有简单的方法呢?
那就是使用宏。

//Reflex.h 

//类申明中添加 classInfo 属性 和 CreatObject、GetClassInfo 方法
#define DECLARE_CLASS(name) \
  protected: \
    static ClassInfo ms_classinfo; \
  public: \
    virtual ClassInfo* GetClassInfo() const; \
    static Object* CreateObject(); 

//实现CreatObject 和 GetClassInfo 的两个方法
#define IMPLEMENT_CLASS_COMMON(name,func) \
  ClassInfo name::ms_classinfo((#name), \
       (ObjectConstructorFn) func); \
             \
  ClassInfo *name::GetClassInfo() const \
    {return &name::ms_classinfo;} 

//classInfo 属性的初始化
#define IMPLEMENT_CLASS(name)      \
  IMPLEMENT_CLASS_COMMON(name,name::CreateObject) \
  Object* name::CreateObject()          \
    { return new name;}

有了宏替换后,我们定义一个新的类。

只需要在类定义中添加 DECLARE_CLASS(classname) 实现中添加IMPLEMENT_CLASS(classname)就可以让这个类实现反射了。

例如我们上面的类B就可以这样写:

class B : public Object
{
  DECLARE_CLASS(B)
public:
  B(){ cout << hex << (long)this << " B constructor!" << endl; }
  ~B(){ cout << hex << (long)this << " B destructor!" << endl; }
};
IMPLEMENT_CLASS(B)

这样不管以后需要添加、修改什么功能都只需要修改宏就可以了而不需要每个类每个类去添加、修改方法。

ok到这里基本上,c++反射机制的实现就大功告成了!。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

(0)

相关推荐

  • 在C++中反射调用.NET的方法(二)

    反射调用返回复杂对象的.NET方法 定义数据接口 上一篇在C++中反射调用.NET(一)中,我们简单的介绍了如何使用C++/CLI并且初步使用了反射调用.NET程序集的简单方法,今天我们看看如何在C++与.NET程序集之间传递复杂对象. 先看看.NET程序集的一个返回对象的方法: public IUserInfo GetUserByID(int userId) { IUserInfo userinfo= EntityBuilder.CreateEntity<IUserInfo>(); user

  • 在C++中反射调用.NET的方法(一)

    为什么要在C++中调用.NET 一般情况下,我们常常会在.NET程序中调用C/C++的程序,使用P/Invoke方式进行调用,在编写代码代码的时候,首先要导入DLL文件,然后在根据C/C++的头文件编写特殊的C#平台调用代码,例如像下面这个样子: [DllImport("Interop.dll",EntryPoint = "Multiply",CharSet = CharSet.Ansi)] static extern int Multiply(int factor

  • 在C++中反射调用.NET的方法(三)

    在.NET与C++之间传输集合数据 上一篇<在C++中反射调用.NET(二)>中,我们尝试了反射调用一个返回DTO对象的.NET方法,今天来看看如何在.NET与C++之间传输集合数据. 使用非泛型集合的委托方法 先看看.NET类中的一个返回列表数据的方法: //返回List或者数组,不影响 C++调用 public List<IUserInfo> GetUsers(string likeName) { List<IUserInfo> users = new List&l

  • Java 反射机制详解及实例代码

    Java反射详解 本篇文章依旧采用小例子来说明,因为我始终觉的,案例驱动是最好的,要不然只看理论的话,看了也不懂,不过建议大家在看完文章之后,在回过头去看看理论,会有更好的理解. 下面开始正文. [案例1]通过一个对象获得完整的包名和类名 package Reflect; /** * 通过一个对象获得完整的包名和类名 * */ class Demo{ //other codes... } class hello{ public static void main(String[] args) {

  • C++ 反射机制详解及实例代码

    C++ 反射机制 一.前言: Java有着一个非常突出的动态相关机制:Reflection,用在Java身上指的是我们可以于运行时加载.探知.使用编译期间完全未知的classes.换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体.或对其fields设值.或唤起其methods.然而C++是不支持反射机制,虽然C++有RTTI(运行时类型识别).但是想要实现C++对象序列化,序列化就是存储到磁盘上,将对象变成一定格式的二

  • Java 反射机制详解及实例

    Java 反射机制详解及实例 反射,当时经常听他们说,自己也看过一些资料,也可能在设计模式中使用过,但是感觉对它没有一个较深入的了解,这次重新学习了一下,感觉还行吧!            一,先看一下反射的概念: 主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义. 反射是Java中一种强大的工具,能够使我们很方便的创建灵活的代码,这些代码可以再运行时装配,无需在组件之间进行源代码链接.但是反射使用不当会成本很高

  • 微信小程序 require机制详解及实例代码

    微信小程序 require机制详解 一, JS模块加载:一次性加载全部JS, 但并不一定立即执行. 先提一提微信小程序架构: 类浏览器 -> HTTP本地服务 -> 云端服务 微信小程序运行的架构,基本上是浏览器 -> HTTP本地服务 -> 云端服务, HTTP本地服务用来读取本地文件或者代理云端的文件资源.读取项目中JS文件, 是由HTTP本地服务取本地存储的脚本文件. 似乎比较简单,一个HTML 引用所有JS文件 既然采用了这种架构,那微信小程序就类似浏览器那样,借助一个HT

  • Java 垃圾回收机制详解及实例代码

     Java 垃圾回收机制详解 乍一看,垃圾回收所做的事情应当恰如其名--查找并清除垃圾.事实上却恰恰相反.垃圾回收会跟踪所有仍在使用的对象,然后将剩余的对象标记为垃圾.牢记了这点之后,我们再来深入地了解下这个被称为"垃圾回收"的自动化内存回收在JVM中到底是如何实现的. 手动管理内存 在介绍现代版的垃圾回收之前,我们先来简单地回顾下需要手动地显式分配及释放内存的那些日子.如果你忘了去释放内存,那么这块内存就无法重用了.这块内存被占有了却没被使用.这种场景被称之为内存泄露. 下面是用C写

  • Linux INotif机制详解及实例代码

    Linux INotif机制 一. 前言: 众所周知,Linux 桌面系统与 MAC 或 Windows 相比有许多不如人意的地方,为了改善这种状况,开源社区提出用户态需要内核提供一些机制,以便用户态能够及时地得知内核或底层硬件设备发生了什么,从而能够更好地管理设备,给用户提供更好的服务,如 hotplug.udev 和 inotify 就是这种需求催生的.Hotplug 是一种内核向用户态应用通报关于热插拔设备一些事件发生的机制,桌面系统能够利用它对设备进行有效的管理,udev 动态地维护 /

  • Android 消息机制详解及实例代码

    Android 消息机制 1.概述 Android应用启动时,会默认有一个主线程(UI线程),在这个线程中会关联一个消息队列(MessageQueue),所有的操作都会被封装成消息队列然后交给主线程处理.为了保证主线程不会退出,会将消息队列的操作放在一个死循环中,程序就相当于一直执行死循环,每循环一次,从其内部的消息队列中取出一个消息,然后回调相应的消息处理函数(handlerMessage),执行完成一个消息后则继续循环,若消息队列为空,线程则会阻塞等待.因此不会退出.如下图所示: Handl

  • JavaScript 闭包机制详解及实例代码

    首先要区分两个概念,一是匿名函数,一是闭包. 所谓匿名函数,就是创建函数没有给定函数名.经常出现的包括函数表达式,就是定义一个匿名函数,然后将函数赋值给某个变量,而此时这个变量就相当于该函数的函数名,例如: var sayHi = function(){ alert("Hi"); }; //注意这个分号 sayHi(); //调用函数 还有一种常用匿名函数的情况是回调函数,如 JQuery 中常用到的: $("p").click(function(){ alert(

  • Java中的反射机制详解

    Java中的反射机制详解 反射,当时经常听他们说,自己也看过一些资料,也可能在设计模式中使用过,但是感觉对它没有一个较深入的了解,这次重新学习了一下,感觉还行吧! 一,先看一下反射的概念: 主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义. 反射是Java中一种强大的工具,能够使我们很方便的创建灵活的代码,这些代码可以再运行时装配,无需在组件之间进行源代码链接.但是反射使用不当会成本很高! 看概念很晕的,继续往下

  • Java动态代理和反射机制详解

    反射机制 Java语言提供的一种基础功能,通过反射,我们可以操作这个类或对象,比如获取这个类中的方法.属性和构造方法等. 动态代理:分为JDK动态代理.cglib动态代理(spring中的动态代理). 静态代理 预先(编译期间)确定了代理者与被代理者之间的关系,也就是说,若代理类在程序运行前就已经存在了,这种情况就叫静态代理 动态代理 代理类在程序运行时创建的代理方式.也就是说,代理类并不是在Java代码中定义的,而是在运行期间根据我们在Java代码中的"指示"动态生成的. 动态代理比

随机推荐