如何使用 void 指针而不是对象访问类的成员变量
Posted
技术标签:
【中文标题】如何使用 void 指针而不是对象访问类的成员变量【英文标题】:How to gain Access to member variables of a class using void pointer but Not Object 【发布时间】:2009-07-10 04:19:22 【问题描述】:我试图在不使用对象的情况下访问类的成员变量。请告诉我该怎么做。
class TestMem
int a;
int b;
public:
TestMem()
void TestMem1()
a = 10;
b = 20;
;
void (TestMem::*pMem)();
int main(int argc, char* argv[])
TestMem o1;
pMem = &(TestMem::TestMem1);
void *p = (void*)&pMem;
// How to access a & b member variables using variable p
getch();
return 0;
【问题讨论】:
这个问题涉及到类,而且肯定不是C,而是C++。 【参考方案1】:执行此操作的“正确”方法是使用来自<stddef.h>
的offsetof()
宏。不幸的是,offsetof()
在 C++ 中有一些相当不错的 draconian restrictions:
由于C++中structs的扩展功能,在这种语言中,
offsetof
的使用仅限于“POD [plain old data] types”,对于类来说,或多或少对应了C中struct的概念(尽管仅具有公共非虚拟成员函数且没有构造函数和/或析构函数的非派生类也可以称为 POD)。
因此,如果您将a
和b
公开并摆脱TestMem
的构造函数,您可以编写类似这样的内容来访问a
:
C++风格:
#include <cstddef>
int vala = *reinterpret_cast<int *>(reinterpret_cast<char *>(&o1)
+ offsetof(TestMem, a));
C 风格:
#include <stddef.h>
int vala = *(int *) ((char *) &o1 + offsetof(TestMem, a));
注意这里需要使用&o1
,而不是p
,它是一个函数指针。 TestMem::TestMem1
的地址与a
和b
的位置没有任何关系。类方法不会驻留在内存中靠近类成员变量的任何位置。
“错误”的方法是猜测a
和b
在内存中的位置。它们很可能分别位于从o1
开始的偏移量 0 和 4。所以这段代码大部分时间都可以工作:
int vala = *(int *) ((char *) &o1 + 0);
int valb = *(int *) ((char *) &o1 + 4);
这里有很多假设。这假定整数是 4 个字节,并且在 a
和 b
之间没有填充。另一方面,它没有上面的任何限制:a
和 b
不需要公开,你可以有一个构造函数,无论如何。
【讨论】:
【参考方案2】:简单的答案:不要这样做。
不可能有任何情况可以证明这样的访问是合理的。只是必须有一个不同的解决方案。
【讨论】:
我完全不同意。动态获取成员变量将非常有用。如果我可以传入我想要访问函数的成员变量,这将有助于高阶编程。实际上,我必须创建一个函数包装器并使用函数指针。【参考方案3】:我想出了一个解决方案,但它脏:
class TestMem
public:
int a;
int b;
TestMem()
void TestMem1()
a = 10;
b = 20;
;
void* offset(void* ptr, ...)
va_list ap;
va_start(ap, ptr); // get 1st argument's address
long i = va_arg(ap, long); // get next argument
va_end(ap);
return (char*)ptr + i;
void test()
TestMem t;
void* p = (TestMem*)&t;
t.a = 8;
t.b = 9;
printf("%i\n", *(int*)offset(p, &TestMem::a));
printf("%i\n", *(int*)offset(p, &TestMem::b));
【讨论】:
【参考方案4】:我想评论 John Kugelman 提供的答案,作为新成员没有足够的声誉,因此将其发布为答案。
offsetof - 是一个 C 函数,用于每个成员都是公共的结构,不确定我们是否可以引用答案中提到的私有变量。
然而,同样可以用一个简单的 sizeof 替换 offsetof,当然,当我们确定数据成员的类型时。
int vala = *reinterpret_cast<int *>(reinterpret_cast<char *>( ptr ) );
int valb = *reinterpret_cast<int *>(reinterpret_cast<char *>( ptr ) + sizeof ( int ) );
据我所知,您将无法访问。 当您分配 p 时,它在这里并没有引用 o1 并且 p 不能替换 (o1.*pMem)() 中的 pMem,因为 p 没有定义为 TestMem 的函数成员。
【讨论】:
【参考方案5】:简短回答:你不能。
长答案:可以,但它高度依赖于实现。 如果你转储你在 *p 找到的内存,你会在附近的某个地方看到你正在寻找的东西——a 和 b。但是您很可能还会看到其他一些东西。这些东西是什么,它意味着什么,它有多大(以及暗示 a 和 b 实际存在的位置)取决于实现。
【讨论】:
【参考方案6】:完全有办法。 C++ 有成员指针,即相对于对象的指针。它们是通过在指针类型上为*
加上前缀T::
来定义的,并通过使用->*
或.*
成员指针访问运算符来使用。所以,是的,它看起来很可怕:)。
class T
int a, b;
public:
typedef int T::* T_mem_ptr_to_int;
static T_mem_ptr_to_int const a_ptr;
static T_mem_ptr_to_int const b_ptr;
;
T::T_mem_ptr_to_int const T::a_ptr = &T::a;
T::T_mem_ptr_to_int const T::b_ptr = &T::b;
int weird_add(T* left, T* right)
return left->*T::a_ptr + right->*T::b_ptr;
这更常用于成员函数指针,看起来像Result (T::*ptr_name)(Arg1, Arg2, ...)
。
【讨论】:
以上是关于如何使用 void 指针而不是对象访问类的成员变量的主要内容,如果未能解决你的问题,请参考以下文章