Объект C ++, Смещение позиции элемента в памяти
Вопрос
Существует ли лучший способ установить позиционное смещение элемента данных объекта, чем следующий?
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, но я иногда все еще использую материал с указателем на элемент.