c++实现反射机制

一、问题的提出

在c++程序中,如果知道了一个类,然后我们就可以使用类名去访问这个类的 静态公有成员变量 或 静态成员函数:

    类名::静态公有成员变量;          	            
    类名::静态公有成员函数;

或者我们可以创建这个类的一个对象,然后通过对象去访问公有成员变量 或 公有成员函数:

	类名 *pobj = new 类名();
	pobj -> 公有成员变量;
	pobj -> 公有成员函数;

问题来了,如果在程序中只知道类名的字符串,怎样访问这个字符串标识的类的成员变量和成员函数呢?即:

	类名 *pobj = new "类名字符串"()

有的时候,在一些场景下,需要将C++类名序列化,序列化就是存储到磁盘上,将类名字符串变成一定格式的二进制编码,然后要用的时候再读出保存在磁盘上的二进制编码的类名字符串,然后怎样根据这个类名字符串创建对应的类对象呢?
这个问题就是反射。我们知道,C++语言本身是不支持反射的。

二、反射是什么?

NET下的很多技术都是基于反射机制来实现的,反射让.NET平台下的语言变得得心应手。最简单的,比如枚举类型,我们我可以很容易的获得一个枚举变量的数值以及其名称字符串。
反射 (Reflection) 是 Java 的特征之一,它允许运行中的 Java 程序获取自身的信息,并且可以操作类或对象的内部属性。

Oracle 官方对反射的解释是:

Reflection enables Java code to discover information about the fields, methods and constructors of loaded classes, and to use reflected fields, methods, and constructors to operate on their underlying counterparts, within security restrictions.
The API accommodates applications that need access to either the public members of a target object (based on its runtime class) or the members declared by a given class. It also allows programs to suppress default reflective access control.

简而言之,通过反射,我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。程序中一般的对象的类型都是在编译期就确定下来的,而 Java 反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。所以我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的。

反射的核心是 JVM 在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。

Java 反射主要提供以下功能:

在运行时判断任意一个对象所属的类;
在运行时构造任意一个类的对象;
在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);
在运行时调用任意一个对象的方法
重点:是运行时而不是编译时

三、使用C++实现反射机制

下面结合工作中用到的反射机制进行讲解。

  1. 首先创建一个基类,这个类的子类类名就是上面说到的需要被序列化的对象:
// Copyright 2019 xxx.
// License(Apache)
// Author:
// Function:

#ifndef FRAMEWORK_PROCESSOR_H_
#define FRAMEWORK_PROCESSOR_H_

#include <iostream>

namespace mynamespace{
class Processor{
public:
    Processor() {}
    virtual ~Processor() {}  
    virtual int Process() = 0;  //返回错误码,返回0表示处理器处理成功,不为0都表示不成功
private:

};
}//namespace mynamespace
#endif
  1. 关键的地方来了:
    设计一个工厂类,这个工厂类主要的作用是 根据给定的类名字符串生成该类名对象。具体的功能包括:
  • 注册功能:把类名字符串和类对象这一对保存到工厂的一个map容器中;

  • 创建对象:根据“ClassXXX”名称来new一个对象

  • 取消注册功能:就是删除map容器中的某一对类名字符串和类对象

// Copyright 2019 xxx.
// License(Apache)
// Author: 
// Function:框架工厂

#pragma once
#ifndef FRAMEWORK_FACTORY_H_
#define FRAMEWORK_FACTORY_H_

#include <map>
#include <string>
#include <iostream>
#include "framework/processor.h"
#include <cstring>
using namespace std;
namespace mynamespace{

/**
 * 处理器类名与实例的映射 (class_map_) 的Key的比较
 */
struct string_cmp {
    bool operator () (const string& a,const string& b) const
    {
        return strcmp(a.c_str(),b.c_str())<0;
    }
};

class Factory
{
public:
    Factory() {
        class_map_.clear();
    }
    ~Factory() {}
    
    /**
     * 注册处理器
     */
    void Register(const string& className, Processor * processor) {
        map<string, Processor *, string_cmp>::iterator iter_ = class_map_.find(className); //先查找map中是否存在处理器
        if (iter_ == class_map_.end()) {   //如果不存在,则插入key-value对
            class_map_.insert(pair<string, Processor *>(className,processor));
        } else {  //如果存在,则更新key对应的value值
            iter_->second = processor;
        }
    }

	/**
     * 获得处理器实例
     */
    Processor * Create(const string& className) {   //通过类名的字符串,返回对应类的对象
        map<string, Processor *,string_cmp>::iterator it = class_map_.find(className);
        if (it == class_map_.end()) {
            cout << "In factory, " << className << " object is no exist.\n";
            return nullptr;
        } else {
            return it->second;
        }
    }

    void Destroy() { //销毁工厂中的处理器对象
        for (iter_ = class_map_.begin(); iter_ != class_map_.end(); iter_++) {
            if(iter_->second) {
                delete (iter_->second);
                (iter_->second) = nullptr;
            }
        }
        class_map_.clear();
    }
private:
    /**
     * 处理器类名与实例的映射
     */
    map<string, Processor *, string_cmp> class_map_;
    map<string, Processor *, string_cmp>::iterator iter_;
};

/**
 * 在具体实现类的头文件添加这个宏,获取类名的方法
 */
#define GETCLASSNAME(str_class_name) \
    public:\
    static string ClassName() {\
        return str_class_name;\
    }\

}//namespace mynamespace

#endif // FRAMEWORK_FACTORY_H_

注意这个类的后面提供了一个宏,这个宏的作用是获取类名的字符串,这个宏要添加到准备通过类名字符串“new”对象的类中。

  1. 从Processor这个基类派生出需要的子类:
    reader.h文件
// Copyright 2019 xxx.
// License(Apache)
// Author: 
// Function:

#ifndef FRAMEWORK_READER_H
#define FRAMEWORK_READER_H

#include "framework/processor.h"
#include "framework/factory.h"
using namespace std;

namespace mynamespace {
class Reader : public Processor {
public:
    Reader() {}
    ~Reader() {}
    int Process();
private:
	GETCLASSNAME("Reader")   //注意要添加获取类名字符串的宏
};
}//namespace mynamespace 
#endif // FRAMEWORK_READER_H

reader.cpp文件:

#pragma hdrstop
#include "reader.h"
#pragma package(smart_init)

namespace mynamespace{
int Reader::Process()
{
	printf("This is reader.\n");
	return 0;
}
}//namespace mynamespace

另一个子类为:
Writer.h文件:

// Copyright 2019 xxx.
// License(Apache)
// Author: 
// Function:

#ifndef FRAMEWORK_WRITER_H
#define FRAMEWORK_WRITER_H

#include "framework/processor.h"
#include "framework/factory.h"
using namespace std;

namespace mynamespace {
class Writer : public Processor {
public:
    Writer() {}
    ~Writer() {}
    int Process();
private:
	GETCLASSNAME("Writer")  //注意要添加获取类名字符串的宏
};
}//namespace mynamespace 
#endif // FRAMEWORK_READER_H

writer.cpp文件:

#pragma hdrstop
#include "writer.h"
#pragma package(smart_init)

namespace mynamespace{
int Writer::Process()
{
	printf("This is writer.\n");
	return 0;
}
}//namespace mynamespace
  1. 创建一个注册类,该类的功能是将 类名字符串 和 类对象 对注册进工厂,每当要添加新的类的时候,都要这个注册类中把新类注册进工厂。
    register.h头文件:
// Copyright 2019 xxx.
// License(Apache)
// Author: 
// Function:

#ifndef FRAMEWORK_REGISTER_H_
#define FRAMEWORK_REGISTER_H_
#include "framework/factory.h"
#include "framework/reader.h"
#include "framework/writer.h"

#define REGISTER(str, pt) pfactory->Register(str, pt);

namespace mynamespace{
class Register
{
public:
	Register() {
		pfactory = new Factory();
	}
	~Register() {
		if(pfactory) {
			delete pfactory;
			pfactory = nullptr;
		}	
	}
    void register();
private:
	Factory *pfactory;
};

} // end mynamespace
#endif // FRAMEWORK_REGISTER_H_

register.cpp源文件:

#include "framework/register.h"

namespace mynamespace{

void Register::register()  {
    /**
     * 处理器具体实现的注册方法,注册到工厂里面
     * TODO :类注册后期放到配置文件中
     */
    REGISTER(Reader::ClassName(), new Reader())
    REGISTER(Writer::ClassName(), new Writer())
}

}  //end mynamespace

好了,至此已经使用C++完成反射机制的实现,下面写一下测试的demo。

  1. 测试demo

test.cpp:

#include "framework/register.h"

int main() {
	Register rg;
	rg.register();
	Factory ft;
	Reader *prd = ft.Create("Reader");  //反射的使用
	prd->Process();  //多态
	Writer *pwt = ft.Create("Writer"); //反射的使用
	pwt->->Process();  //多态
	return 0;
}

编译运行输出也很简单,就是:

This is reader.
This is writer.

6.总结
很多人都认为反射在实际的 开发应用中并不广泛,其实不然。当我们在使用 IDE(如 VS2019,Eclipse,pycharm)时,当我们输入一个对象或类并想调用它的属性或方法时,一按点号,编译器就会自动列出它的属性或方法,这里就会用到反射。

反射最重要的用途就是开发各种通用框架。很多框架(比如 Spring)都是配置化的(比如通过 XML 文件配置 Bean),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射,运行时动态加载需要加载的对象。

当我们编译好程序部署应用后,不再方便修改程序编译,这时可以使用反射,通过修改配置文件中的类名字符串,从而达到访问想要的属性和方法。

  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值