ссылочные переменные и наследование - PullRequest
1 голос
/ 22 ноября 2011

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

#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 ]

3 голосов
/ 22 ноября 2011

Вы вызываете оператор присваивания по умолчанию для obj, который все еще имеет тип Child1, с аргументом типа Child2. Сам объект все еще имеет тип Child1. Вы можете убедиться в этом, реализовав operator = во всех 3 классах и вставив туда оператор print.

1 голос
/ 22 ноября 2011

Два фундаментальных свойства в C ++: созданный объект никогда не меняет своего типа, а инициализированная ссылка всегда ссылается на один и тот же объект.

Здесь происходит то, что вы вызываете компиляторпоставляется не виртуальным operator= для Parent, что почти наверняка не то, что вы хотели.В более общем смысле, однако, присваивание и наследование не работают вместе (именно потому, что вы не можете изменить тип объекта);В большинстве случаев при использовании наследования вы должны запретить присваивание (например, наследуя от boost::noncopyable).Можно реализовать семантику значений для полиморфного класса, используя идиому буквы / конверта, но это сложное решение и редко подходящее.

(Я мог бы добавить, что ваш код не компилируется с компилятором C ++. Вы инициализируете ссылку на неконстантное с помощью временного, что недопустимо в C ++. Допускается, что это расширение Microsoft.)

1 голос
/ 22 ноября 2011
Parent & obj = Child1();

Вы создаете ссылку на объект типа Child1. Это все равно что сказать

Child1 c1;
Parent& obj = c1;

obj теперь просто другое имя для c1, который является объектом типа Child1.

obj = Child2();
obj.func();

Теперь это все равно, что сказать

c1 = Child2();
c1.func();

так что вы видите, что вы все еще вызываете func для объекта типа Child1.

1 голос
/ 22 ноября 2011

Ссылки не могут быть пересозданы - они ссылаются на один и тот же объект в течение всего срока их службы.Если вы хотите что-то, что может изменить объект, на который он ссылается, тогда вам нужно использовать [умный] указатель вместо ссылки.

Здесь вы делаете нарезку экземплярChild2, присвоив его экземпляру Child1.

0 голосов
/ 22 ноября 2011

То, что вы делаете, должно давать ошибку при компиляции. Вы не можете назначить временную переменную (созданную Child2()) для ссылочной переменной. Вы должны создать экземпляр до Child2 и назначить эту переменную для благоговения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...