起因在于,今天在写UE4插件时,有一个对象的protected成员我想要访问。这个类没有提供接口来访问那个成员,并且这个类是定义在引擎代码中的而我不想“污染”引擎代码。
不过,我想到这么做或许可以在不改变原有类的定义下访问其中的protected成员:
定义一个原有类的子类,在其中定义可以访问protected成员的接口。想要访问对象的成员时,将原有类类型的指针转变为子类类型的指针,然后就可以执行刚才定义的操作了。经过尝试,这种操作是没问题的:
#include<iostream> using namespace std; //原有的类: class OriginClass { public: OriginClass(int InData) { Data = InData; } protected: int Data;//想要访问的成员 }; //为了访问protected成员而特意定义的类 class OriginClass_Access : public OriginClass { public: int GetData() { return Data; }//定义的访问成员的方法 }; int main() { //原有的对象: OriginClass* origin = new OriginClass(9); //转换指针: OriginClass_Access* origin_Access = (OriginClass_Access*)origin; //访问成员: cout << origin_Access->GetData() << endl; }输出:
9将一个父类指针直接转换为一个子类指针,看起来是“危险”的。但这里我定义的子类不包含任何额外的字段,只有额外的方法,因此在这里进行的转换是我确保“安全”的。
网上查阅后发现,这种方法也有其他人想到,在《一种直接访问C++对象的protected成员的通用方法_温研的专栏 (探索OS内核的奥秘)-博客》这篇博客中甚至将这种方法定义成了一个宏,使得使用变得更加便利。
下面,我也想仿照它的方法,来提炼出通用的宏来方便使用:
这个宏将会声明一个子类,再声明一个函数用来访问类的protected成员:
#define PROTECTED_MEMBER_ACCESS_FUNCTION_DEFINE(ClassType,MemberName) \ class ClassType##_##MemberName##_Accessor : public ClassType\ {\ public:\ typedef decltype(MemberName) MemberType;\ MemberType Get() { return MemberName; }\ };\ ClassType##_##MemberName##_Accessor::MemberType Get_##ClassType##_##MemberName(ClassType* origin)\ {\ ClassType##_##MemberName##_Accessor* Accessor = (ClassType##_##MemberName##_Accessor*)origin;\ return Accessor->Get();\ }\使用时,先用这个宏来声明函数:
PROTECTED_MEMBER_ACCESS_FUNCTION_DEFINE(OriginClass,Data)随后,就可以通过函数来访问了:
int main() { //原有的对象: OriginClass* origin = new OriginClass(9); //访问成员: cout << Get_OriginClass_Data(origin) << endl; }输出:
9完整代码:
#include<iostream> using namespace std; //这个宏将会声明一个子类,再声明一个函数用来访问一个类的protected成员 #define PROTECTED_MEMBER_ACCESS_FUNCTION_DEFINE(ClassType,MemberName) \ class ClassType##_##MemberName##_Accessor : public ClassType\ {\ public:\ typedef decltype(MemberName) MemberType;\ MemberType Get() { return MemberName; }\ };\ ClassType##_##MemberName##_Accessor::MemberType Get_##ClassType##_##MemberName(ClassType* origin)\ {\ ClassType##_##MemberName##_Accessor* Accessor = (ClassType##_##MemberName##_Accessor*)origin;\ return Accessor->Get();\ }\ //原有的类: class OriginClass { public: OriginClass(int InData) { Data = InData; } protected: int Data;//想要访问的成员 }; //定义访问OriginClass的Data成员的函数: PROTECTED_MEMBER_ACCESS_FUNCTION_DEFINE(OriginClass,Data) int main() { //原有的对象: OriginClass* origin = new OriginClass(9); //访问成员: cout << Get_OriginClass_Data(origin) << endl; }这么看来,难道C++里的protected权限其实是“形同虚设”吗?
仔细想想也并不是这样,因为上面这种方法依赖于:“原生类”是可继承的,而这实际上是不一定的。
在UE4中,一个模块中的类/函数是需要标记为模块_API宏,其他模块才可以得到在cpp中的定义。这样,如果一个类的构造函数的内容是在cpp中的,则另一个模块是无法继承这个类的,因为并不知道父类的构造函数的定义。