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