Как получить доступ к переменным-членам класса, используя пустой указатель, но не объект - PullRequest
1 голос
/ 10 июля 2009

Я пытаюсь получить доступ к переменным-членам класса без использования объекта. пожалуйста, дайте мне знать, что делать.

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;
}

Ответы [ 6 ]

5 голосов
/ 10 июля 2009

«Правильный» способ сделать это - использовать макрос offsetof() из <stddef.h>. К сожалению, offsetof() имеет некоторые драконовские ограничения в C ++:

Из-за расширенной функциональности структур в C ++ в этом языке использование offsetof ограничено «типами POD [обычные старые данные]», которые для классов более или менее соответствуют концепции 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 находятся в памяти. Скорее всего, они имеют смещения 0 и 4 от начала o1 соответственно. Так что этот код будет работать большую часть времени:

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

Здесь много предположений. Это предполагает, что целые числа составляют 4 байта и что между a и b нет заполнения. С другой стороны, он не имеет никаких ограничений сверху: a и b не должны быть публичными, вы можете иметь конструктор, что угодно.

3 голосов
/ 10 июля 2009

Простой ответ: не делай этого.

Просто не может быть ситуации, когда вы могли бы оправдать такой доступ. Просто должно быть другое решение.

1 голос
/ 10 июля 2009

Я хотел бы прокомментировать ответ, предоставленный Джоном Кугельманом, поскольку у нового участника не было достаточной репутации, поэтому я опубликовал его как ответ.

offsetof - это функция C, используемая со структурами, где каждый член является публичным, не уверенным, можем ли мы ссылаться на частные переменные, как указано в ответе.

Однако того же можно добиться, заменив offsetof простым sizeof, конечно, когда мы уверены в типе элементов данных.

int vala = *reinterpret_cast<int *>(reinterpret_cast<char *>( ptr ) );
int valb = *reinterpret_cast<int *>(reinterpret_cast<char *>( ptr ) + sizeof ( int ) );

Насколько мне известно, вы не сможете получить доступ. К тому времени, когда вы присвоили p, это не относится к o1 здесь и p не может заменить pMem в (o1. * pMem) (), так как p не определен как член функции для TestMem.

1 голос
/ 10 июля 2009

Я нашел решение, но оно грязно :

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));
}
0 голосов
/ 16 апреля 2010

Там есть способ. 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, ...).

0 голосов
/ 10 июля 2009

Краткий ответ: Вы не можете.

Длинный ответ: Вы можете, но это сильно зависит от реализации. Если вы сбросите память, найденную в * p, вы увидите где-то там, что вы ищете - a и b. Но вы, скорее всего, увидите и другие вещи. Что это такое, что это значит, насколько оно велико (и, как следствие, где фактически живут a и b), зависит от реализации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...