C++ 实现反射机制

1. 什么是反射

学过 Java 或 C# 的同学应该都知道“反射”机制,很多有名的框架都用到了反射这种特性。这是一种很牛逼的特性,简单的理解就是只根据类的名字就可以获取到该类的实例。有人会说,这不是多此一举吗?直接 new 一个出来不就行了吗?像下面这样:

class Person {
public:
   virtual void show() = 0;
}

class Allen : public Person {
  virtual void show() {
    std::cout << "Hello, I'm Allen!" << std::endl;
  }
}

Person *p = new Allen();
p->show();

可是有时候,你定义好接口 Person 后,你并不知道谁将会实现该接口,甚至不知道什么时候会实现它。所以此时你无法通过 new 操作符来实例化对象。比如未来的某个时候有人编写了一个类叫 Luffy,但是此时你不可能实例化 Luffy 这个类,所以你只能编写下面这种代码:

std::string className = /*从配置文件中读取*/
Person *p = getNewInstance<Person>(className);

你的程序可以从配置文件中读取到 "Luffy" 这个字符串保存到变量 className 中。接下来使用函数 getNewInstance 就可以获取到 Luffy 实例化的对象。

2. C++ 实现反射

很遗憾的是 C++ 并没有直接从语言上提供这种特性给我们用,不过无所不能的 C++ 可以通过一些 trick 来实现反射这种机制。

2.1 引例

直观上,我们可以把 getNewInstance 的实现交给未来要使用我们的框架的人:

template<typename T>
T* getNewInstance(const std::string& className) {
  if (className == "Allen") {
    return new Allen();
  }
  else if (className == "Luffy") {
    return new Luffy();
  }
}

如此一来,一旦有新的类实现,我们就必须得修改这个函数,这很容易出错,维护性很差。

2.2 对象工厂

对象工厂是一种可以间接实例化对象的类。比如:

class ObjectFactory{
  virtual ReflectObject* newInstance() = 0;
}

class ObjectFactory_Allen : public ObjectFactory{
  ReflectObject* newInstance(){
    // 这里注意一点就是,所有能够被反射的类都继承自 ReflectObject 这个类。
    return new Allen();  
  };
}

所以,如果有了对象工厂的实例,我们就可以不断的产生对象了。如下:

ObjectFactory *of = new ObjectFactory_Allen();
// 有了对象工厂的实例后,就可以产生 Allen 对象的实例了
ReflectObject *allen= of->newInstance();
// 接下来可以使用类型转换,把 allen 对象转换成 Person
Person *p = dynamic_cast<Person*>(allen);

2.3 反射器

反射器实际上也是一个类,它管理了类名到对象工厂实例之间的映射关系。反射器对象在程序中是一个全局唯一的对象。这种映射关系看起来就像一张表:

("Allen", Allen 的对象工厂的实例对象);
("Luffy", Luffy 的对象工厂的实例对象);
("Zoro",  Zoro  的对象工厂的实例对象);
……

只要未来你想要编写一个新的类,比如 Allen 类,那么你就必须要同时编写 Allen 的工厂类 ObjectFactory_Allen,同时,你还得实例化一个这个工厂类的对象 objectFactory_Allen = new ObjectFactory_Allen(),并将 ("Allen", objectFactory_Allen) 这种映射关系保存到反射器中。

反射器的定义如下:

class Reflector
{
public:
    Reflector();
    ~Reflector();
    // 如果你要反射你的类,就必须将你的类名,以及工厂实例对象注册到反射器中。
    void registerFactory(const std::string& className, ObjectFactory *of);
    // 反射器可以根据对象工厂实例来生产实例对象。请参考 2.2 节
    ReflectObject* getNewInstance(const std::string& className);
private:
    std::map<std::string, ObjectFactory*> objectFactories;
};

2.4 编写要被反射类的大致思路

有了上面的基础好,要想让你的类被反射,大致有以下几个步骤:

  • 让你的类继承某个接口(该接口继承自 ReflectObject 类)
  • 编写一个对应的工厂类,并实例化一个工厂类对象
  • 调用反射器的 registerFactory 接口,将你的类名和工厂类对象保存到反射器中

特别的,上述第 2 和 3 步很明显是属于成年不变的步骤,它可以使用宏函数来实现。在你完成步骤 1 后,就可以使用宏函数一次完成。在这里,将步骤2 和 3 统一称为“注册反射类”。

2.5 注册反射类

注册反射类实际上就是 2.4 中的第 2 和 3 步,其代码如下:

  • 步骤 1:定义 Allen 类(略)
  • 步骤 2:定义 Allen 的工厂类
class ObjectFactory_Allen : public ObjectFactory{
  ReflectObject* newInstance(){
    return new Allen();  
  };
}
  • 步骤 3:创建 Allen 工厂类实例对象并注册到反射器中
// 函数 reflector 可以用来获取反射器对象。
reflector().registerFactory("Allen", new ObjectFactory_Allen()); 

为了能让上面的代码复用,使用宏函数来进行改写:

/***********需要被反射的类,需要在其对应的 cpp 文件中进行反射声明***********/
#define REFLECT(name)\
class ObjectFactory_##name : public ObjectFactory{\
public:\
  ReflectObject* newInstance() {\
    return new name(); \
  }\
}; \
class Register_##name{\
public:\
  Register_##name(){\
    reflector().registerFactory(#name, new ObjectFactory_##name()); \
  }\
};\
Register_##name register_##name;

这样,以后步骤 2 和 步骤 3 就可以简写成:

REFLECT(Allen);

看起来是不是很方便?

上面的宏函数并没有直接调用反射器的注册函数,而是先定义了一个注册类对象,在注册类对象的构造函数中完成了调用,然后又定义了一个该类的全局对象,以达到自动调用构造函数的目的。想一想,为什么不直接调用反射器的注册函数?

3. 完整示例

3.1 客户端部分

  • main 函数
#include "Person.h"
#include "Reflector.h"


int _tmain(int argc, _TCHAR* argv[])
{
    Person *allen = getNewInstance<Person>("Allen");
    Person *luffy = getNewInstance<Person>("Luffy");
    allen->show();
    luffy->show();

    delete allen;
    delete luffy;
    return 0;
}
  • 用户编写的 Allen 类和 Luffy 类
#include "Person.h"

class Allen : public Person
{
public:
    Allen()
    virtual ~Allen();
    virtual void show();
};

class Luffy : public Person
{
public:
    Luffy();
    virtual ~Luffy();
    virtual void show();
};
//两个类的实现,在 cpp 文件中

REFLECT(Allen);// 注册反射类,只能写在 cpp 文件中。

Allen::Allen()
{
    std::cout << "Allen()" << std::endl;
}


Allen::~Allen()
{
    std::cout << "~Allen()" << std::endl;
}

void Allen::show()
{
    std::cout << "Hello, I'm Allen" << std::endl;
}


REFLECT(Luffy); // 注册反射类,只能写在 cpp 文件中。

Luffy::Luffy()
{
    std::cout << "Luffy()" << std::endl;
}


Luffy::~Luffy()
{
    std::cout << "~Luffy()" << std::endl;
}

void Luffy::show()
{
    std::cout << "Hello, I'm Luffy" << std::endl;
}

3.2 框架部分

  • Person 接口定义与实现
// Person.h
#include "Reflector.h"

// 让 Person 继承反射基类
class Person : public ReflectObject
{
public:
    Person();
    virtual ~Person();
    virtual void show();
};

// Person.cpp
Person::Person()
{
    std::cout << "Person()" << std::endl;
}


Person::~Person()
{
    std::cout << "~Person()" << std::endl;
}

void Person::show()
{
    std::cout << "Hello, I'm person" << std::endl;
}
  • 反射器部分
// Reflect.h
#pragma once

#include <string>
#include <map>
#include <iostream>

/********************所有需要实现反射的类需要继承它************************/
class ReflectObject { 
public:
    virtual ~ReflectObject(){}
};
/************************************************************************/


/******************对象工厂抽象类,用来生成对象实例************************/
class ObjectFactory {
public:
    ObjectFactory(){ std::cout << "ObjectFactory()" << std::endl; }
    virtual ~ObjectFactory(){ std::cout << "~ObjectFactory()" << std::endl; }
    virtual ReflectObject* newInstance() = 0;
};
/************************************************************************/


/***********反射器,用来管理(对象名,对象工厂)的映射关系******************/
class Reflector
{
public:
    Reflector();
    ~Reflector();
    void registerFactory(const std::string& className, ObjectFactory *of);
    ReflectObject* getNewInstance(const std::string& className);
private:
    std::map<std::string, ObjectFactory*> objectFactories;
};
/************************************************************************/


/**********************获取反射器实例,全局唯一****************************/
Reflector& reflector();
/************************************************************************/


/***********需要被反射的类,需要在其对应的 cpp 文件中进行反射声明***********/
#define REFLECT(name)\
class ObjectFactory_##name : public ObjectFactory{\
public:\
    ObjectFactory_##name(){ std::cout << "ObjectFactory_" << #name << "()" << std::endl; }\
    virtual ~ObjectFactory_##name(){ std::cout << "~ObjectFactory_" << #name << "()" << std::endl; }\
    ReflectObject* newInstance() {\
        return new name(); \
    }\
}; \
class Register_##name{\
public:\
    Register_##name(){\
        reflector().registerFactory(#name, new ObjectFactory_##name()); \
    }\
};\
Register_##name register_##name;
/************************************************************************/



/***********************根据类名获取对象实例******************************/
template<typename T>
T* getNewInstance(const std::string& className) {
    return dynamic_cast<T*>(reflector().getNewInstance(className));
}
/************************************************************************/
// Reflector.cpp
#include "Reflector.h"

Reflector::Reflector()
{
}


Reflector::~Reflector()
{
    std::map<std::string, ObjectFactory*>::iterator it = objectFactories.begin();
    for (; it != objectFactories.end();++it)
    {
        delete it->second;
    }
    objectFactories.clear();
}


void Reflector::registerFactory(const std::string& className, ObjectFactory *of)
{
    std::map<std::string, ObjectFactory*>::iterator it = objectFactories.find(className);
    if (it != objectFactories.end()) {
        std::cout << "该类已经存在……" << std::endl;
    }
    else {
        objectFactories[className] = of;
    }
}



ReflectObject* Reflector::getNewInstance(const std::string& className)
{
    std::map<std::string, ObjectFactory*>::iterator it = objectFactories.find(className);
    if (it != objectFactories.end()) {
        ObjectFactory *of = it->second;
        return of->newInstance();
    }
    return NULL;
}

// 用来获取反射器对象,注意这是全局唯一的。
Reflector& reflector() {
    static Reflector reflector;
    return reflector;
}

3.3 运行结果


这里写图片描述

4. 总结

反射机制的实现,主要在于工厂模式的灵活使用。另外,还需要掌握一些 C++ 代码技巧,比如如何在 main 函数之前进行初始化操作,反射器的初始化以及生产工厂类对象很好的体现了全局对象的应用。

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值