Вот код:
#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
вызвался бы метод базового класса, независимо от того, что он указывет на обьект производного(содержит его адрес).
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Всем приветВообщем задачка нужно сделать маску как на https://www
У меня есть коллекция классов с полями
Ошибка возникает при вызове метода Save(), подскажите в чем причина? Пользователь создаеться без ошибок, а запись не проходит, хотя файл каким...