2013-08-24 2 views
3

У меня есть простой код, который не работает корректно со ссылкой (полиморфизм).Полиморфизм в C++ работает неправильно со ссылкой

#include <iostream> 
#include <string> 

class Base { 
public: 
    Base() {} 
    virtual ~Base() {} 
    virtual std::string text() const { 
     return "Base"; 
    } 
}; 

class Derived: public Base { 
public: 
    Derived(Base& _b): b(_b) {} 
    virtual ~Derived() {} 
    virtual std::string text() const { 
     return b.text() + " - Derived"; 
    } 

private: 
    Base& b; 
}; 

int main(int argc, char const *argv[]) 
{ 
    Base b; 
    Derived d1(b); 
    std::cout << d1.text() << std::endl; 

    Derived d2(d1); 
    std::cout << d2.text() << std::endl; 
    return 0; 
} 

И выход:

Base - Derived 
Base - Derived 

Вторая строка в выводе я ожидал: Base - Derived - Derived. Я прочитал некоторые ресурсы, и полиморфизм отлично работает со ссылкой и указателем, но в этой ситуации это не так. Если я заменил ссылку на указатель, он снова будет работать. Итак, кто-нибудь может дать мне некоторые объяснения?

Большое спасибо!

+2

Похоже, вы вызываете копию-ctor по умолчанию 'Derived', поскольку вы никогда не предоставляли ее. Значение по умолчанию лучше подходит во втором примере, чем первый (который явно является типом 'Base'). Чтобы доказать это, установите точку останова или выведите debug msg в 'Derived (Base &)'. Вы должны увидеть, что во втором примере он не запускается. Другими словами, d1 является просто копией d2. – WhozCraig

ответ

7

вы вызываете конструктор копирования по умолчанию Derived. Поэтому при завершении d2 будет простой экземпляр участника d1, и оба их члена b будут ссылаться на тот же экземпляр Base.

Чтобы доказать это, добавьте это к вашему Derived класса

class Derived: public Base { 
public: 
    Derived(Derived& d) : b(d) {} 
    Derived(Base& _b): b(_b) {} 
    virtual ~Derived() {} 
    virtual std::string text() const { 
     return b.text() + " - Derived"; 
    } 

private: 
    Base& b; 
}; 

С этим ваш выход будет:

Base - Derived 
Base - Derived - Derived 

И только отметить, что это не великая идея или звездный пример обучения полиморфизма. (Но это интересный пример переопределения конструкции). Также обратите внимание, что это НЕ является типичным переопределением по умолчанию copy-construction (где параметр является const-ref-type). Таким образом, часть причины этого не является самой большой выборкой.

0

Как отмечено в комментарии, теперь используется конструктор копирования по умолчанию, и это является причиной вашего наблюдения с тем же выходом для обоих. Таким образом, d1 просто копируется в d2, а не используется для переменной базового элемента внутри d2.

1

Ваш d1 и d2 оба имеют тип Derived, так что это работает правильно. Обычно ссылки обращаются вспять; например

Base b; 
Derived d; 
Base &dr = d; 

std::cout << b.text() << std::endl; 
std::cout << dr.text() << std::endl; 

Здесь text() вызывается через Base типа, но последний будет вызывать версию в Derived.

Обратите внимание, что обычно не имеет смысла разрешать инициализацию производного класса через базовый класс. Предположим, вы добавили тип Derived2, который обладает способностями или отличается от Derived. Этот конструктор разрешил бы

Derived2 d2; 
Derived d1(d2); 

который, вероятно, очень плохая идея.

+1

Я считаю, что он это знает, и он задавался вопросом, почему нынешний путь не работает. – lpapp

1

Если вы указали код, вы увидите, что при вызове Derived d2(d1) производный Derived :: Derived (Base &) не вызывается. Это связано с тем, что аргумент d1 является лучшим совпадением для конструктора неявных копий , который просто копирует элемент b из d1 в d2.

Для того, чтобы увидеть поведение, которое вы ожидаете, вы можете явно наложить d1 на (Base&)d1. Если вы так что вы получите код, подобный следующему (с приборами):

#include <iostream> 
#include <string> 

class Base { 
public: 
    Base() {} 
    virtual ~Base() {} 
    virtual std::string text() const { 
     return "Base"; 
    } 
}; 

class Derived: public Base { 
public: 
    Derived(Base& _b): b(_b) {std::cout << "init'ed with: " << _b.text() << std::endl;} 
    virtual ~Derived() {} 
    virtual std::string text() const { 
     return b.text() + " - Derived"; 
    } 

private: 
    Base& b; 
}; 

int main(int argc, char const *argv[]) 
{ 

    std::cout << "Creating Base" << std::endl; 
    Base b; 

    std::cout << "Creating d1" << std::endl; 
    Derived d1(b); 
    std::cout << d1.text() << std::endl; 

    std::cout << "Creating d2" << std::endl; 
    Derived d2(d1); 
    std::cout << d2.text() << std::endl; 

    std::cout << "Creating d3" << std::endl; 
    Derived d3((Base&)d1); 
    std::cout << d3.text() << std::endl; 

    return 0; 
} 

И это дает ожидаемый результат:

Creating Base 
Creating d1 
init'ed with: Base 
Base - Derived 
Creating d2 
Base - Derived 
Creating d3 
init'ed with: Base - Derived 
Base - Derived - Derived 
+0

Ugh. Другая причина не иметь конструктора D (B &)! – sfjac

Смежные вопросы