c ++ Multiple Inheritance — компилятор, модифицирующий мои указатели

Если я запускаю следующий код, я получаю разные адреса. Зачем?

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) {}
};

Кроме того, если какой-либо базовый класс будет пустым, проблема исчезнет.
Это ошибка компилятора? Я хочу, чтобы мои указатели остались в покое.

4

Решение

Если я запускаю следующий код, я получаю разные адреса. Зачем?

Поскольку 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* указатель. Множественное наследование усложняет ситуацию — поэтому многие люди предлагают избегать его, если в этом нет крайней необходимости.

3

Другие решения

Конструктор объединения никогда не инициализирует член 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";
2

По вопросам рекламы [email protected]