Вот код:
#include <iostream>
class BaseClass
{
public:
virtual void f() const;
};
void BaseClass::f() const
{
std::cout << "In base class function\n";
}
class DerivedClass: public BaseClass
{
public:
void f() const override;
};
void DerivedClass::f() const
{
std::cout << "In derived class function\n";
}
int main()
{
DerivedClass derived_object;
BaseClass base_object = derived_object;
BaseClass *base_object_ptr = &derived_object;
base_object.f();//вызовет f, определённый в суперклассе
base_object_ptr -> f();//вызовет f, определённый в наследнике
return 0;
}
В консоле сначала выведется In base class function
, а затем In derived class function
.
Виртуальные функции обеспечивают динамическое связывание (dynamic binding) и объектно-ориентированное программирование.
Решение о вызове виртуальной функции принимается на этапе выполнения программы, а не на этапе компиляции, как это происходит с невиртуальными функциями. Но такое поведение будет поддерживаться только при использовании ссылок или указателей.
В случае
base_object.f();
base_object
не является ни ссылкой, ни указателем. Следовательно решение о вызове функции будет принято еще на этапе компиляции (как и наблюдаем - вызывается BaseClass::f
).
При использовании указателя все работает как задумывалось - вызывается метод из DerivedClass
, который перекрывает соответствующий виртуальнуй метод базового класса. Каким образом это сделано - детали реализации.
Второй момент - это конструирование BaseClass base_object = derived_object;
Что происходит в этом месте? На самом деле вызывается конструктор базового класса. Это копирующий конструктор, который сгенерирует компилятор (см. Правило трёх/пяти). Он имеет сигнатуру: BaseClass(const BaseClass&)
. Т.к. DerivedClass
открыто наследует класс BaseClass
, объект этого класса (DerivedClass
) может быть преобразован к ссылке на BaseClass
. Этим объясняется успех операции. Наглядно это можно увидеть, если объявить копирующий конструктор в BaseClass
и сделать в нем вывод. Пример тут - https://wandbox.org/permlink/gITmHAUWMcksSeZP
Но такое явление чаще всего является неправильным, т.к. оно делает невозможным использование того самого полиморфизма, ради которого и создавались эти виртуальные методы. Это явление называют срезкой (slicing).
Одним предложением: Это главная особенность полимофизма времени выполнения. Но, а если подробно...
Производный класс может неявно преобразиться в базовый, так как базовый класс является его фундаментом (при преобразовании скопируется только та часть, что лежит в фундаменте, но в вашем вопросе это не имеет значения). Поэтому строкой:
BaseClass base_object = derived_object;
вы получаете вполне нормальный обьект типа BaseClass
и дальше в коде вызываете его метод.
Теперь рассмотрим указатель базового класса, который вполне может содержать адрес обекта производного:
BaseClass* base_object_ptr = &derived_object;
Каждый обьект полиморфного типа, неявно содержит указатель на виртуальную таблицу, поэтому вызывая base_object_ptr->f();
вызывется виртуальный метод обьекта derived_object
, а не метод базового класса. Вот если в базовом классе убрать виртуальность функции f
, тогда посредством указателья base_object_ptr
вызвался бы метод базового класса, независимо от того, что он указывет на обьект производного(содержит его адрес).
Виртуальный выделенный сервер (VDS) становится отличным выбором
Всем приветВообщем задачка нужно сделать маску как на https://www
У меня есть коллекция классов с полями
Ошибка возникает при вызове метода Save(), подскажите в чем причина? Пользователь создаеться без ошибок, а запись не проходит, хотя файл каким...