По определению, представление объекта содержит все данные, хранящиеся в объекте, включая все нестатические c члены данных и базовые классы, виртуальные или нет. Определение объектного представления в C ++ 17:
последовательность N unsigned char
объектов, принимаемых объектом типа * 1009. *, где N равно sizeof(T)
.
Очевидно, что любые байты, которые используются для хранения подобъектов, «заняты» объектом, поскольку подобъект является частью объект. Это означает, что эти байты являются частью представления объекта. Надеюсь, это ответит на ваш Q1.
Что касается Q2, я не думаю, что стандарт гарантирует, что все полные объекты одного типа будут иметь одинаковый макет, если только тип не является стандартным макетом. На практике я думаю, что было бы необычно найти реализацию, в которой полные объекты одного типа могли бы иметь разные макеты. Если каждая единица перевода видит одно и то же определение класса (что и должно быть, в противном случае вы нарушаете ODR), тогда у компилятора не должно быть проблем с генерацией одного и того же макета в каждой единице перевода, и это кажется разумным. (в противном случае вам может потребоваться создать несколько таблиц). Но, если по какой-то причине реализация действительно хотела изменить макет, я думаю, что она могла бы сделать это даже в пределах одной единицы перевода. разрешено, даже если макет был гарантирован! Если T
не является ни стандартным макетом, ни тривиально копируемым типом, то мне неясно, разрешено ли вам вообще выполнять арифметику указателя c внутри объекта типа T
, используя char*
, который указывает в один из байтов T
. Рассмотрим, например, что offsetof
гарантированно поддерживается только для типов со стандартной компоновкой, а memcpy
вложение объекта в байтовый массив и обратно гарантированно будет четко определено только для тривиально копируемых типов. Скажем, у типа есть виртуальный базовый класс, поэтому он не является ни стандартным, ни легко копируемым. Я не уверен, что этот код четко определен:
struct B {};
struct D : virtual B {};
auto foo() {
D d;
B* pb = &d;
return reinterpret_cast<char*>(pb) - reinterpret_cast<char*>(&d);
}
Если он вообще не определен для вызова foo
, то вопрос о том, всегда ли он возвращает одно и то же значение, очевидно, спорный . Возможно, смещения внутри этих типов просто не наблюдаемы (согласно правилам абстрактной машины).