简单的C++反射实现

java和c#等高级语言拥有反射机制,可以通过字符串很方便的生成对应实体。这极大的方便了对象的序列化及对象的生产效率。C++开发人员一直想要这么一个机制,希望仅通过配置文件就可以改变程序的行为却不需要修改代码。现实是,我们将希望寄托在工厂类上,需要修改行为,则需要修改工厂的生产产品。尽管将修改集中于一处或者几处,但仍旧是修改了代码,不够完美。所以C++能不能拥有反射机制呢?答案是有的,虽然语言本身不支持,但是我们可以自食其力。
反射说白了,是通过字符串去产生对应的类对象,也就是说字符串和产生类的方法之间需要有一个映射关系。我们在C++中暂且由std::map来实现。键值可以用类名转换成的字符串来担当,除此之外还需要一个产生对象的函数。这个函数应该具有通用性,因为所有的具有反射功能的类都需要插入键值对到定义好的std::map中。
我们可以暂定这个函数具有如下形式:

typedef void * (*CreateObj)();

这个函数返回一个void*的指针,可以承载所有的类。另外,CreateObj是不带参数的,这是我们为实现动态生成对象必须要遵循的约束,那就是所有需要进行反射的类需要提供默认构造函数,否则无法初始化。
另外,为了生成对象,CreateObj必须知道每个类的存在,这并不合理。实际上来说,每个CreateObj只要知道自己所要生成的对象即可,所以我们完全可以在每个类当中定义这么一个静态函数,用于类对象的生成。
我们的数据结构已经出来了,那就是:

std::map<std::string, CreateObj> m_mapCreator;

紧接着,我们需要一个对象来管理这个数据结构,我们期望这个类可以管理新的反射关系的添加,可以为我们提供一个通过类名字符串就返回类对象的函数:

class CRefect
{
public:
    typedef void* (*CreateObject)();
    static CRefect* instance()
    {
        static CRefect cr;
        return &cr;
    }

    template<class T>
    T* Create(std::string t)
    {
        return (T*)(m_mapCreat[t]());
    }

    void* registerProperty(std::string key, CreateObject pC)
    {
        m_mapCreat[key] = pC;
        return NULL;
    }
private:
    std::map<std::string, CreateObject> m_mapCreat;
};

上面的类定义简洁明了,我们设计成单例,保证了映射表的唯一性。另外提供了注册键值对的函数,提供了动态生成对象的函数。看到上面定义,有人会问为什么registerProperty会有void*的返回值,意义何在?
这个问题可以和另外一个问题一起回答:
何时对需要反射的类进行注册?
我们最希望在程序运行到main函数之前就对所有的类进行注册。C++恰好有这么一种内置机制,那就是static变量的初始化,是先于我们的应用逻辑的。
好,解决了上面的所有疑问,我们设定以下两个宏:

// 声明classname具有反射能力
#define DECLARE_DYNAMIC(classname)\
    static void*  CreateObject()\
    {\
        return new classname;\
    }\
    static void* s_null; // 为每个类设定一个静态指针,编译器要求我们对其进行初始化。
    初始化的时候正是我们注册映射关系的好时机。但是我们并不需要s_null有真正的意义,
    所以,将registerProperty的返回值设定为void*,并返回为null

// 对classname的反射能力进行实现,注册映射关系
#define IMPL_DYNAMIC(classname)\
    void* classname::s_null = CRefect::instance()->registerProperty(#classname, &classname::CreateObject);

最后我们看看怎么来使我们的自定义类拥有反射能力呢?

class CTest
{
public:
    void print()
    {
        cout<<"print"<<endl;
    }

    DECLARE_DYNAMIC(CTest);
};

IMPL_DYNAMIC(CTest);

测试一下:

int _tmain(int argc, _TCHAR* argv[])
{
    CTest* gt = CRefect::instance()->Create<CTest>("CTest");
    gt->print();
    getchar();
    AA a;
    delete gt;
    return 0;
}

至此,一个简单的反射框架就写好了。可以看到,使用也是非常方便。
其实为了使用方便,我们甚至可以更进一步:

#define DYNAMIC_CREATE(classname) \
    CRefect::instance()->Create<classname>(#classname);

CTest* gt = DYNAMIC_CREAE(CTest);使用将更加简洁。

能力有限,难免有疏忽之处。欢迎广大程序员朋友提出宝贵意见。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值