Вопрос

Существует ли лучший способ установить позиционное смещение элемента данных объекта, чем следующий?

class object
{
  int a;
  char b;
  int c;
};

object * o = new object();
int offset = (unsigned char *)&(object->c) - (unsigned char *)o;
delete o;
Это было полезно?

Решение

В этом случае вашим классом является POD, поэтому вы можете использовать offsetof макрос из <cstddef>.

На практике, в большинстве реализаций, для большинства классов, вы можете использовать тот же трюк, который обычно использует offsetof:

int offset = &(((object *)0)->c) - (object *)0;

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

Помните также, что если ваш класс имеет какое-либо множественное наследование, то для (по крайней мере) всех базовых значений, кроме одного, (void*)(base*)some_object != (void*)(derived*)some_object.Поэтому вы должны быть осторожны с тем, к чему применяете смещение.Пока вы вычисляете и применяете его относительно указателя на класс, который фактически определяет поле (то есть не пытайтесь вычислить смещение поля базового класса по указателю производного класса), у вас почти наверняка все будет в порядке.Нет никаких гарантий относительно расположения объекта, но большинство реализаций делают это очевидным способом.

Технически для любого класса, отличного от POD, не имеет смысла говорить о "смещении" поля от базового указателя.Разница между указателями не обязательно должна быть одинаковой для всех объектов этого класса.

Другие советы

На самом деле нет необходимости создавать объект:

(size_t)&(((object*)0)->c)

То есть адрес элемента в объекте с нулевым адресом - это смещение этого элемента в объект.

Конечно, вам понадобится доступ к участнику, поэтому вам нужно либо сделать его struct или добавить public: модификатор доступа.

Вот как offsetof реализован, по крайней мере, для большинства компиляторов.

Вместо указателя.
Вы можете использовать указатель на элемент.

class X;
typedef int X::*    PtrToMemberInt;  // Declare a pointer to member.

class X
{
    int a;
    char b;
    float c;

    public:
    static PtrToMemberInt   getAPtr()
    {
        return &X::a;
    }

    int d;
};

int main()
{
    // For private members you need to use a public method to get the address.
    PtrToMemberInt aPtr = X::getAPtr();

    // For public members you can take the address directly;
    PtrToMemberInt dPtr = &X::d;


    // Use the pointer like this:
    X   a;
    a.*aPtr = 5;
    a.*dPtr = 6;
}

Чтобы добавить к ответу Мартинса, как указано в "Дизайне и эволюции C ++" Страуструпа (раздел [13.11]):

указатели на элементы данных зарекомендовали себя как полезный способ выражения структуры класса C ++ в реализации [Hübel, 1992]

Сандип был так любезен, что преобразовал оригинальную статью и сделал ее доступной на http://sandeep.files.wordpress.com/2008/07/ps.pdf

Обратите внимание, что описанная реализация предшествовала C ++ RTTI, но я иногда все еще использую материал с указателем на элемент.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top