Мне в основном интересно, как С++ выдает объект в памяти. Итак, я слышал, что динамические трансляции просто настраивают указатель объекта в памяти со смещением; и переинтерпретировать вид позволяет нам делать что-либо с этим указателем. Я этого не понимаю. Детали будут оценены!
Компоновка памяти объекты С++
Ответ 1
Каждый класс излагает свои члены данных в порядке объявления.
Компилятору разрешено размещать отступы между членами, чтобы сделать доступ эффективным (но он не разрешен для повторного заказа).
Как работает dynamic_cast<>
- это деталь реализации компилятора и не определяется стандартом. Все это будет зависеть от ABI, используемого компилятором.
reinterpret_cast<>
работает, просто изменяя тип объекта. Единственное, что вы можете гарантировать, что работает, - это то, что наведение указателя на void * и обратно на тот же указатель на класс даст вам тот же указатель.
Ответ 2
Макет памяти в основном оставлен для реализации. Ключевым исключением является то, что переменные-члены для данного спецификатора доступа будут в порядке их объявления.
§ 9.2.14
Нестатические члены данных (неединичного) класса с одинаковым доступом (раздел 11) распределяются так, что более поздние члены имеют более высокий адресов внутри объекта класса. Порядок распределения нестатических члены данных с различным контролем доступа не определены (11). Требования к выравниванию реализации могут приводить к двум соседним членам не выделяться сразу после друг друга; так что требования к пространству для управления виртуальными функциями (10.3) и виртуальные базовые классы (10.1).
Помимо переменных-членов, классу или структуре необходимо предоставить пространство для переменных-членов, подобъектов базовых классов, управления виртуальными функциями (например, виртуальную таблицу) и заполнения и выравнивания этих данных. Это зависит от реализации, но спецификация Itanium ABI является популярным выбором. gcc и clang придерживаются (по крайней мере, до степени).
http://mentorembedded.github.io/cxx-abi/abi.html#layout
Itanium ABI, конечно же, не является частью стандарта С++ и не является обязательным. Чтобы получить более подробную информацию, вам нужно обратиться к документации и инструментам разработчика. clang предоставляет инструмент для просмотра макета памяти классов. В качестве примера:
class VBase {
virtual void corge();
int j;
};
class SBase1 {
virtual void grault();
int k;
};
class SBase2 {
virtual void grault();
int k;
};
class SBase3 {
void grault();
int k;
};
class Class : public SBase1, SBase2, SBase3, virtual VBase {
public:
void bar();
virtual void baz();
// virtual member function templates not allowed, thinking about memory
// layout and vtables will tell you why
// template<typename T>
// virtual void quux();
private:
int i;
char c;
public:
float f;
private:
double d;
public:
short s;
};
class Derived : public Class {
virtual void qux();
};
int main() {
return sizeof(Derived);
}
После создания исходного файла, использующего макет памяти класса, clang покажет макет памяти.
$ clang -cc1 -fdump-record-layouts layout.cpp
Макет для Class
:
*** Dumping AST Record Layout
0 | class Class
0 | class SBase1 (primary base)
0 | (SBase1 vtable pointer)
8 | int k
16 | class SBase2 (base)
16 | (SBase2 vtable pointer)
24 | int k
28 | class SBase3 (base)
28 | int k
32 | int i
36 | char c
40 | float f
48 | double d
56 | short s
64 | class VBase (virtual base)
64 | (VBase vtable pointer)
72 | int j
| [sizeof=80, dsize=76, align=8
| nvsize=58, nvalign=8]
Подробнее об этой функции clang можно найти в блоге Eli Bendersky:
http://eli.thegreenplace.net/2012/12/17/dumping-a-c-objects-memory-layout-with-clang/
gcc предоставляет аналогичный инструмент, `-fdump-class-hierarchy '. Для класса, указанного выше, он печатает (между прочим):
Class Class
size=80 align=8
base size=58 base align=8
Class (0x0x141f81280) 0
vptridx=0u vptr=((& Class::_ZTV5Class) + 24u)
SBase1 (0x0x141f78840) 0
primary-for Class (0x0x141f81280)
SBase2 (0x0x141f788a0) 16
vptr=((& Class::_ZTV5Class) + 56u)
SBase3 (0x0x141f78900) 28
VBase (0x0x141f78960) 64 virtual
vptridx=8u vbaseoffset=-24 vptr=((& Class::_ZTV5Class) + 88u)
Он не перечисляет переменные-члены (или, по крайней мере, я не знаю, как их получить), но вы можете сказать, что они должны быть между смещением 28 и 64, точно так же, как в макете clang.
Вы можете видеть, что один базовый класс выделяется как primary
. Это устраняет необходимость в настройке указателя this
, когда к Class
обращается как SBase1
.
Эквивалент для gcc:
$ g++ -fdump-class-hierarchy -c layout.cpp
Эквивалент для Visual С++:
cl main.cpp /c /d1reportSingleClassLayoutTest_A
Ответ 3
Ответ: "Это сложно". Динамический бросок не просто настраивает указатели со смещением; он может фактически получить внутренние указатели внутри объекта, чтобы выполнить свою работу. GCC следует за ABI, разработанным для Itanium, но реализуется более широко. Здесь вы можете найти подробные сведения: Itanium С++ ABI.
Ответ 4
Как указывалось ранее, полная информация сложна, болезненна для чтения и действительно полезна только разработчикам компилятора и варьируется между компиляторами. В принципе, каждый объект содержит следующее (обычно в этом порядке):
- Информация о времени выполнения
- Не виртуальные базовые объекты и их данные (возможно, в порядке объявления).
- Переменные участника
- Виртуальные базовые объекты и их данные (возможно, в порядке поиска дерева DFS).
Эти фрагменты данных могут быть или не могут быть дополнены, чтобы упростить выравнивание памяти и т.д. Скрытые в информации о времени выполнения данные относятся к типу, v-таблицам для виртуальных родительских классов и т.д., все из которых являются специфичными для компилятора.
Когда дело касается кастингов, reinterpret_cast
просто изменяет тип данных С++ указателя и ничего не делает, поэтому вам лучше быть уверенным, что вы знаете, что вы делаете, когда используете его, иначе вы несете беспорядок идет плохо. dynamic_cast
делает то же самое, что и static_cast (при изменении указателя), за исключением того, что использует информацию типа времени выполнения, чтобы выяснить, может ли она быть применена к данному типу и как это сделать. Опять же, все это специфично для компилятора. Обратите внимание, что вы не можете dynamic_cast
a void*
, потому что ему нужно знать, где найти информацию типа времени выполнения, чтобы он мог выполнять все свои замечательные проверки времени выполнения.
Ответ 5
на этот вопрос уже ответил http://dieharddeveloper.blogspot.in/2013/07/c-memory-layout-and-process-image.html вот отрывок оттуда: В середине адресного пространства процесса для общих объектов сохраняется область. Когда создается новый процесс, диспетчер процессов сначала отображает два сегмента из исполняемого файла в память. Затем он декодирует заголовок ELF программы. Если заголовок программы указывает, что исполняемый файл был связан с общей библиотекой, диспетчер процессов (PM) будет извлекать имя динамического интерпретатора из заголовка программы. Динамический интерпретатор указывает на общую библиотеку, содержащую код компоновщика времени выполнения.