Ссылочные переменные и наследование
Следующий код:
#include <stdio.h>
class Parent
{
public:
virtual void func() {printf("Parent\n");}
};
class Child1 : public Parent
{
virtual void func() {printf("Child1\n");}
};
class Child2 : public Parent
{
virtual void func() {printf("Child2\n");}
};
int main(int argc, char* argv[])
{
Parent & obj = Child1();
obj.func();
obj = Child2();
obj.func();
return 0;
}
дает следующие результаты:
expected: Child1 Child2.
actual: Child1 Child1.
(составлено на VS2010)
Я предполагаю, что vptr не изменяется назначением. Есть ли способ заставить его быть воссозданным (кроме использования указателя на Parent и присвоения ему с помощью new)?
Спасибо
5 ответов
Вы вызываете оператор присваивания по умолчанию для obj, который по-прежнему имеет тип Child1, с аргументом типа Child2. Сам объект по-прежнему имеет тип Child1
, Вы можете проверить это, внедрив operator =
на всех 3 классах, и вставив там оператор печати.
Ссылки не могут быть повторно установлены - они ссылаются на один и тот же объект в течение всего срока их службы. Если вы хотите что-то, что может изменить объект, на который он ссылается, тогда вам нужно использовать [умный] указатель вместо ссылки.
То, что вы делаете здесь, нарезает экземпляр Child2
присвоив его экземпляру Child1
,
Два фундаментальных свойства в C++: созданный объект никогда не меняет своего типа, а инициализированная ссылка всегда ссылается на один и тот же объект.
Здесь происходит то, что вы называете компилятор не виртуальным operator=
за Parent
что почти наверняка не то, что вы хотели. В более общем смысле, однако, присваивание и наследование не работают вместе (именно потому, что вы не можете изменить тип объекта); В большинстве случаев при использовании наследования вы должны запретить присваивание (наследуя от boost::noncopyable
, например). Можно реализовать семантику значений для полиморфного класса, используя идиому буквы / конверта, но это сложное решение и редко подходящее.
(Могу добавить, что ваш код не компилируется с помощью компилятора C++. Вы инициализируете ссылку на неконстантное значение с помощью временного, что недопустимо в C++. Допускается, что это расширение Microsoft.)
Parent & obj = Child1();
Вы создаете ссылку на объект типа Child1
, Это как сказать
Child1 c1;
Parent& obj = c1;
obj
теперь просто другое имя для c1
, который является объектом типа Child1
,
obj = Child2();
obj.func();
Теперь это как сказать
c1 = Child2();
c1.func();
так что вы видите, вы все еще звоните func
на объекте типа Child1
,
То, что вы делаете, должно давать ошибку при компиляции. Вы не можете назначить временную переменную (созданный Child2()
) к ссылочной переменной. Вы должны создать экземпляр до Child2
и присвойте эту переменную почтению.