Если я запускаю следующий код, я получаю разные адреса. Зачем?
class Base1 {
int x;
};
class Base2 {
int y;
};
class Derived : public Base1, public Base2 {
};
union U {
Base2* b;
Derived* d;
U(Base2* b2) : b(b) {}
};
int main()
{
Derived* d = new Derived;
cout << d << "\n";
cout << U(d).d << "\n";
return 0;
}
Еще интереснее, если вы неоднократно входите и выходите из объединения, адрес продолжает увеличиваться на 4, вот так
int main()
{
Derived* d = new Derived;
cout << d << "\n";
d = U(d).d;
cout << d << "\n";
d = U(d).d;
cout << d << "\n";
return 0;
}
Если объединение будет изменено следующим образом, проблема исчезнет
union U {
void* v;
Base2* b;
Derived* d;
U(void* v) : v(v) {}
};
Кроме того, если какой-либо базовый класс будет пустым, проблема исчезнет.
Это ошибка компилятора? Я хочу, чтобы мои указатели остались в покое.
Если я запускаю следующий код, я получаю разные адреса. Зачем?
Поскольку Base2
подобъект Derived
объект не находится в начале Derived
объект. Так что адреса разные. Когда компилятор выполняет неявное приведение Derived*
к Base2*
, он должен настроить адрес.
Учитывая определения Base1
а также Base2
классы, оба подобъекта Derived
класс не может быть по начальному адресу Derived
объект — по этому адресу нет места для обоих подобъектов.
Скажем, у вас был этот код:
Derived* d = new Derived;
Base1* pb1 = d;
Base2* pb2 = d;
Как это было бы возможно для pb1
а также pb2
указать на тот же адрес? pb1
должен указать на Base1::x
пункт и pb2
должен указать на Base2::y
элемент (и эти элементы должны быть различны).
Еще интереснее, если вы неоднократно входите и выходите из объединения, адрес продолжает увеличиваться на 4
Потому что вы читаете из профсоюза d
член после написания b
член, который является неопределенным поведением (вы, по сути, выполняете что-то вроде reinterpret_cast<Derived*>()
на Base2*
).
Я хочу, чтобы мои указатели остались в покое.
Нет, если вы хотите Base2*
указатель. Множественное наследование усложняет ситуацию — поэтому многие люди предлагают избегать его, если в этом нет крайней необходимости.
Конструктор объединения никогда не инициализирует член d
В конструкторе объединения есть ошибка, при которой вместо инициализации члена b параметром b2 он инициализирует b с собой
// b(b) should probably be b(b2)
U(Base2* b2) : b(b) {}
Когда ваш первый пример основной функции пытается создать экземпляр U и напечатать член d, он фактически печатает неопределенное значение, потому что член d не был инициализирован и не гарантированно будет доступен.
// U(d) doesn't construct member d, so .d returns an undefined value
cout << U(d).d << "\n";
Относительно вашего второго примера основной функции
// d is set to a newly constructed instance of Derived
Derived* d = new Derived;
// current address of d is printed
cout << d << "\n";
// a new instance of U is constructed. The address of member d will be in close
// proximity to the newly initialized U instance, and is what will be printed
d = U(d).d;
cout << d << "\n";
// yet another new instance of U is constructed, and again, the address of member
// d will be in close proximity to the newly initialized U instance, and is
//what will be printed
d = U(d).d;
cout << d << "\n";