如何使用 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)。

因此,如果您将ab 公开并摆脱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));

注意这里需要使用&amp;o1,而不是p,它是一个函数指针。 TestMem::TestMem1 的地址与ab 的位置没有任何关系。类方法不会驻留在内存中靠近类成员变量的任何位置。


“错误”的方法是猜测ab 在内存中的位置。它们很可能分别位于从o1 开始的偏移量 0 和 4。所以这段代码大部分时间都可以工作:

int vala = *(int *) ((char *) &o1 + 0);
int valb = *(int *) ((char *) &o1 + 4);

这里有很多假设。这假定整数是 4 个字节,并且在 ab 之间没有填充。另一方面,它没有上面的任何限制:ab 不需要公开,你可以有一个构造函数,无论如何。

【讨论】:

【参考方案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:: 来定义的,并通过使用-&gt;*.* 成员指针访问运算符来使用。所以,是的,它看起来很可怕:)。

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 指针而不是对象访问类的成员变量的主要内容,如果未能解决你的问题,请参考以下文章

通过指向派生类的指针访问派生成员变量

123213

线程安全访问成员变量

static变量以及方法

C++ 使用变量访问类的公共成员

如何使用指向类的成员函数的指针(详解!)