Ссылка на базовый класс - присвоить ему другой тип
Что происходит в следующем примере?
struct B { };
struct D1 : B { };
struct D2 : B { };
int main()
{
D1 d;
D2 d2;
B& x = d;
x = d2;
}
Я знаю, что ссылка не переустанавливается. x
по-прежнему относится к d
, но тогда как вы можете назначить d2
на d
?
Еще несколько:
struct B
{
B () { x = 0; }
int x;
virtual void foo () { cout << "B" << endl; }
};
struct D1 : B
{
D1 () { x = 1; }
virtual void foo () { cout << "D1" << endl; }
};
struct D2 : B
{
D2 () { x = 2; }
virtual void foo () { cout << "D2" << endl; }
};
int main()
{
D1 d;
D2 d2;
B& x = d;
x.foo(); //D1
//x.x is 1 here
x = d2;
x.foo(); //also D1
//but x.x is 2 here
}
Кажется, что x.x
был обновлен, но vftable не был... Почему?
Ответы
Ответ 1
x
относится к субобъекту B
базового класса d
. Назначение x = d2
фрагментов базового подобъекта B
из d2
и присваивает его значение подобъекту d
.
Это обычно не делается намеренно.
EDIT:
Кажется, что x.x обновлен, но vftable не был... Почему?
Это то, что делает оператор присваивания B::operator=
. Базовые классы в С++ полностью не знают, что они базовые классы. Кроме того, тип объекта не может быть изменен в течение всего срока его службы. Ближайшей альтернативой является С++ 11 std::move
, которая может переносить старый объект B
внутри D1
в новый d2
объект. Затем вы уничтожили бы старый объект.
Ответ 2
Если вы хотите, вы можете реализовать the = самостоятельно и "избегать" нарезки, проверив соответствующий конкретный тип (или давая ошибку). Пример с ошибками приведен ниже.
struct B {
virtual B& operator = (B& b) = 0;
};
struct D1 : B {
D1& operator = (B& b) {
if ( dynamic_cast<D1*>(&b) == 0 ) {
cerr << "Cannot assign non D1 to D1" << endl;
exit(255);
}
// handle the assignments
return *this;
}
};
struct D2 : B {
int c;
D2& operator = (B& b) {
if ( dynamic_cast<D2*>(&b) == 0 ) {
cerr << "Cannot assign non D2 to D2" << endl;
exit(255);
}
// handle the assignments
return *this;
}
};
Ответ 3
В вашем случае, когда вы назначаете этот способ, члены, которые не относятся к базовому классу, будут нарезаны. Это означает, что в этом случае он копируется, как если бы вы назначали один объект базового класса другому.