2017-01-19 36 views
1

Может кто-нибудь сказать мне, что не так с моей программой ниже? Я использую переменную-элемент ссылки в классе для полиморфизма. Я ожидаю, что второй cout скажет «производный 2», но он говорит «база»;полиморфизм с переменным ссылочного элемента

#include <iostream> 

// Example program 
#include <iostream> 
#include <string> 
class base 
{ 
public: 
    virtual void print(){ std::cout<<"base"<<std::endl;} 
}; 
class derived: public base 
{ 
public: 
    virtual void print(){ std::cout<<"derived"<<std::endl;} 
}; 

class derived2: public base 
{ 
    virtual void print(){ std::cout<<"derived2"<<std::endl;} 
}; 

class foo 
{ 
public: 
    base & bar; 
    base boo; 
    derived foobar; 
    derived2 foobar2; 
    foo(): bar(boo){} 
    void newfoo(base & newfoo){ bar = newfoo; bar.print();} 
}; 
int main() 
{ 
    foo test; 
    test.bar.print(); 
    test.newfoo(test.foobar2); 
} 

Выход: база база

+0

Вы должны знать, что почти никогда не хотите использовать ссылки в качестве переменных-членов. –

ответ

2

Как уже упоминалось, вы не можете переназначить ссылку.
Всякий раз, когда вы делаете что-то вроде bar = newfoo, вы не сбрасываете ссылку. Вместо этого вы вызываете operator= для bar с newfoo в качестве аргумента.
Таким образом, в вашем случае вы находитесь , нарезая свои объекты и (скажем), копируя свою base часть в bar.


Вид ссылочного типа инструмента, к которому вы можете переназначить существует в стандартной библиотеке шаблонов, и это называется std::reference_wrapper.
Это следует пример, основанный на коде, который использует его и имеет ожидаемое поведение:

#include<functional> 
#include <iostream> 
#include <string> 

class base 
{ 
public: 
    virtual void print() { std::cout<<"base"<<std::endl;} 
}; 
class derived: public base 
{ 
public: 
    virtual void print(){ std::cout<<"derived"<<std::endl;} 
}; 

class derived2: public base 
{ 
    virtual void print(){ std::cout<<"derived2"<<std::endl;} 
}; 

class foo 
{ 
public: 
    std::reference_wrapper<base> bar; 
    base boo; 
    derived foobar; 
    derived2 foobar2; 
    foo(): bar(boo){} 
    void newfoo(base & newfoo){ bar = newfoo; bar.get().print();} 
}; 
int main() 
{ 
    foo test; 
    test.bar.get().print(); 
    test.newfoo(test.foobar2); 
} 

В этом случае operator= фактически вызывает связывание ссылку на данный объект. В любом случае, как вы можете видеть, в этом случае вы должны вызвать get для доступа к базовой ссылке.

Примечание: отложите пример выше, ваш код не является типичным прецедентом для std::reference_wrapper.
Я упомянул об этом только ради полноты.

+0

Связаны ли ссылки с полиморфизмом? Интернет заставляет его звучать как указатель, а ссылки работают одинаково с полиморфизмом. – sdstack

+0

@sdstack Ссылки и указатели - это предназначенные инструменты, которые будут использоваться при использовании полиморфных типов. В противном случае вы можете столкнуться с проблемами нарезки. – skypjack

0

bar является ссылкой на boo и boo имеет тип base все, что вы назначаете ему.

Назначение может изменять значение переменных, а не его тип.

1

Вы не можете переназначить ссылку. Когда оператор присваивания используется со ссылкой, он присваивает базовое значение.

Таким образом, bar = newfoo; просто присваивает foo типа base до foobar, разрезая его в процессе.

У вас может быть другое поведение, если вы замените ссылки указателями, которые могут быть переназначены.

0

Полиморфизм не работает со ссылками. Попробуйте следующее:

#include <iostream> 

// Example program 
#include <iostream> 
#include <string> 
class base 
{ 
public: 
    virtual void print(){ std::cout << "base" << std::endl; } 
}; 
class derived : public base 
{ 
public: 
    virtual void print(){ std::cout << "derived" << std::endl; } 
}; 

class derived2 : public base 
{ 
    virtual void print(){ std::cout << "derived2" << std::endl; } 
}; 

class foo 
{ 
public: 
    base* bar; 
    foo(): bar(0) {} 
    void newfoo(base* newfoo){ bar = newfoo; bar->print(); } 
}; 

int main() { 
    foo test; 
    test.newfoo(new derived2); 
}