C++ 在外部访问对象的protected成员的方法

tech2022-07-17  149

起因

起因在于,今天在写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中的,则另一个模块是无法继承这个类的,因为并不知道父类的构造函数的定义。

最新回复(0)