вызывать дочернюю версию функции вместо родительской?

#c #inheritance #virtual #overriding

#c #наследование #виртуальный #переопределение

Вопрос:

Хорошо, итак, у меня есть два класса.

 class a{
public:
a(){};
void print(){cout << "hello"};
}

class b : public a{
public:
void print(){cout << "hello world";}
}
  

И массив родителей с дочерним

 a blah[10];
blah[5] = b();
  

Затем я вызываю print и хочу, чтобы он сказал «привет, мир».

 blah[5].print();
  

Но она вызывает родительскую. Как мне это исправить?

Ответ №1:

Это можно исправить, объявив функцию виртуальной, а-ля:

 class a{
public:
    virtual void print(){
        cout << "hello";
    }
}

class b : public a{
public:
    virtual void print() {
        cout << "hello world";
    }
}
  

Вот как реализуется полиморфизм в C . Подробнее здесь: http://en.wikipedia.org/wiki/Virtual_function

Однако следует отметить, что в вашем примере она никогда не вызовет дочернюю функцию, потому что вы используете значения объекта, а не указатели / ссылки на объекты. Чтобы исправить это,

 a * blah[10];
blah[5] = new b();
  

Затем:

 blah[5]->print();
  

Ответ №2:

То, что вы ищете, — это полиморфизм во время выполнения, который означает, что объект принимает «множество форм» (т. Е. a или b) и действует соответствующим образом по мере выполнения программы. В C это делается путем создания функции virtual в базовом классе a :

 virtual void print() {cout << "hello"}; 
  

Затем вам нужно сохранить элементы по указателю или ссылке, и — поскольку в общем случае производные классы могут вводить новые элементы данных и им требуется больше места для хранения — обычно объекты в куче создаются с помощью new :

 a* blah[10];
blah[5] = new b();
  

Затем вы можете вызвать:

 blah[5]->print();  
  

И это вызовет b реализацию print() .

Вам следует сделать это позже delete blah[5] (и любые другие, на которые вы указали в памяти, возвращаемой new ).

На практике неплохо использовать контейнер, который может удалять содержащиеся в нем объекты, когда он сам уничтожается, будь то из-за выхода из области видимости или удаления. std::vector<> является одним из таких контейнеров. Вы также можете использовать интеллектуальные указатели для автоматизации удаления объектов a и b . Это помогает сделать код корректным, если перед выполнением ваших delete инструкций генерируются исключения, и вы хотите, чтобы ваша программа продолжала работать без утечки памяти. Библиотека boost — это самое простое / наилучшее место для получения реализации интеллектуального указателя. Вместе:

 #include <vector>
#include <boost/shared_ptr.hpp>

std::vector<boost::shared_pointer<a> > blah(10);

blah[5] = new b();
  

(Более нормально использовать векторы с push_back() , поскольку это автоматически увеличивает вектор, чтобы он соответствовал всем добавленным вами элементам, при этом новое общее количество доступно по вызову vector::size() .)

Ответ №3:

Это происходит потому, что вы сообщили компилятору, что ваш экземпляр имеет тип a . Она находится в массиве a объектов, верно? Значит, она имеет тип a !

Конечно, вы хотите, чтобы метод в b перезаписал метод в a , несмотря на наличие ссылки на родительский тип. Вы можете добиться такого поведения, используя virutal ключевое слово при объявлении функции в родительском классе.

 virtual void print(){cout << "hello"};
  

Почему это так работает?

Потому что, когда вы приводите свой объект к родительскому классу, вы вводите двусмысленность. Когда вызывается этот объект print() , как мы должны с этим обращаться? Она имеет тип b , но ссылка имеет тип a , поэтому код округления может ожидать, что она будет вести себя как a , а не b как!

Для устранения неоднозначности вводится virtual ключевое слово. virtual функции всегда переопределяются, если объект относится к дочернему классу, содержащему метод с такой же сигнатурой.

Ура!

Ответ №4:

Объявить a::print() как виртуальную и использовать указатель / ссылку для вызова print() функции. Когда вы это делаете blah[5] = b() , выполняется нарезка объекта. У вас нет никакого эффекта при вызове виртуальной функции с использованием object.