Ссылочные переменные и наследование

Следующий код:

#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и присвойте эту переменную почтению.

Другие вопросы по тегам