服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - C++ 反射机制详解及实例代码

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

2021-04-28 14:43C++教程网 C/C++

这篇文章主要介绍了C++ 反射机制详解及实例代码的相关资料,需要的朋友可以参考下

C++ 反射机制

一.前言:

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

?
1
ClassT* obj = FactoryCreate("ClassT");

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

二.实现:

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

?
1
2
3
4
5
class Object
{
public:
  virtual string ToString() = 0;
};

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

?
1
2
3
4
5
class MyClass :public Object
{
public:
  virtual string ToString(){ return "MyClass"; }
};

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
  ...
}

我们使用就可以这样:

?
1
2
3
4
5
6
7
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作为需要支持反射机制类的基类

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

然后是实现:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//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类就大功告成了:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//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初始化。

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

?
1
2
3
4
5
6
7
8
9
10
11
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) 传入类名即可。

?
1
2
3
4
5
6
int main()
{
  Object* obj = Object::CreateObject("B");
  delete obj;
  return 0;
}

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

三.使用宏简化代码:

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//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就可以这样写:

?
1
2
3
4
5
6
7
8
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++反射机制的实现就大功告成了!。

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

原文链接:http://blog.csdn.net/y1196645376/article/details/51455273

延伸 · 阅读

精彩推荐
  • C/C++C语言main函数的三种形式实例详解

    C语言main函数的三种形式实例详解

    这篇文章主要介绍了 C语言main函数的三种形式实例详解的相关资料,需要的朋友可以参考下...

    ieearth6912021-05-16
  • C/C++关于C语言中E-R图的详解

    关于C语言中E-R图的详解

    今天小编就为大家分享一篇关于关于C语言中E-R图的详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看...

    Struggler095962021-07-12
  • C/C++使用C++制作简单的web服务器(续)

    使用C++制作简单的web服务器(续)

    本文承接上文《使用C++制作简单的web服务器》,把web服务器做的功能稍微强大些,主要增加的功能是从文件中读取网页并返回给客户端,而不是把网页代码...

    C++教程网5492021-02-22
  • C/C++c/c++内存分配大小实例讲解

    c/c++内存分配大小实例讲解

    在本篇文章里小编给大家整理了一篇关于c/c++内存分配大小实例讲解内容,有需要的朋友们可以跟着学习参考下。...

    jihite5172022-02-22
  • C/C++c/c++实现获取域名的IP地址

    c/c++实现获取域名的IP地址

    本文给大家汇总介绍了使用c/c++实现获取域名的IP地址的几种方法以及这些方法的核心函数gethostbyname的详细用法,非常的实用,有需要的小伙伴可以参考下...

    C++教程网10262021-03-16
  • C/C++C语言实现双人五子棋游戏

    C语言实现双人五子棋游戏

    这篇文章主要为大家详细介绍了C语言实现双人五子棋游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    两片空白7312021-11-12
  • C/C++OpenCV实现拼接图像的简单方法

    OpenCV实现拼接图像的简单方法

    这篇文章主要为大家详细介绍了OpenCV实现拼接图像的简单方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    iteye_183805102021-07-29
  • C/C++深入C++拷贝构造函数的总结详解

    深入C++拷贝构造函数的总结详解

    本篇文章是对C++中拷贝构造函数进行了总结与介绍。需要的朋友参考下...

    C++教程网5182020-11-30