Пример OP (немного упрощенный)
struct Base1 {
int b1;
};
struct Base2 {
int b2;
};
struct Der: Base1, Base2 { };
может привести к следующему макету памяти:
// start of Der
// start of Base1
0000: Base1::b1 // type int
// start of Base2
0004: Base2::b2 // type int
Итак, когда struct Der
является экземпляром части его содержимого является экземпляром struct Base2
, но он не начинается с того же адреса, что и экземпляр Der
.
С
Der *ptr = new Der();
инициализация
Base2 *b2 = ptr;
не приводит к простой копии адреса от ptr
до b2
. Также присутствует неявное преобразование из Der*
в Base2*
. Компилятору известно о соотношении классов Der
и Base2
. Следовательно, преобразование приводит к беззвучному добавлению смещения Base2
в Der
.
Чтобы показать это в действии, я сделал небольшую демонстрацию. (Я не уверен, насколько это убедительно.):
#include <iostream>
struct Base1 {
int b1 = 1;
};
struct Base2 {
int b2 = 2;
};
struct Der: Base1, Base2 { };
int main()
{
Der *ptr = new Der;
Base2 *b2;
std::cout << "ptr:" << ptr << ", ptr1->b2: " << ptr->b2 << '\n';
b2 = ptr;
std::cout << "b2: " << b2 << ", b2->b2: " << b2->b2 << '\n';
}
Скомпилировано с помощью g cc 4.1.2, вы можете найти следующий код, в котором происходит фактическое присвоение:
mov %rax, QWORD PTR [%rbp-24] # %rbp-24 <- storage of ptr on stack
add %rax, 4
mov QWORD PTR [%rbp-32], %rax # %rbp-32 <- storage of b2 on stack
Живая демонстрация в CompilerExplorer
Примечание:
При компиляции с использованием самой современной версии компилятора будет выдано аналогичное add
, но также и множество других вещей, которые не так просто расшифровать (на глаз), чем сгенерированный код более старой версии. Следовательно, я выбрал самый старый gcc
, который смог найти.