Идентификатор
#новичкам
Продолжаем серию постов! Ранее мы уже упоминали идентификатор со специальным значением
Запретить переопределение метода в наследниках можно с помощью идентификатора
Ограничивать переопределения виртуальных методов может показаться неочевидным действием. Казалось бы, вот к чему это? Этот вопрос стоит рассматривать с технической и смысловой точки зрения. Начнем с последнего.
Код, который вы пишите, может быть достаточно глубоко осмыслен и выразителен. В частности, в семействе вашего класса можно выделить подсемейство со специфичным и фиксированным поведением.
В качестве примера, давайте рассмотрим семейство датчиков умного дома. Пусть была разработана открытая библиотека, которая предоставляет некоторый набор интерфейсов для разных типов датчиков. Например, для подсемейства пожарных датчиков. Производители устройств могут наследовать специальный интерфейс и реализовать прошивку для своего девайса. Как у них работает этот сенсор — никто не знает, но главное, что в случае срабатывания такого датчика происходит важное, в рамках этого подсемейства, действие - вызывается бригада пожарных. Это достаточно важный смысл, который может быть заложен в наследника класса и ограничен в переопределении для производителя:
Думаю, обосновывать важность этого ограничения не стоит. Если производитель по каким-то причинам захочет вызвать бригаду стриптизёров вместо пожарных, то до жаркой вечеринки дело не дойдёт!
Конечно, можно написать комментарии к коду, в надежде, что их прочитают... Но вот практика показывает, что их периодически игнорируют. Если какое-то ограничение не срабатывает во время компиляции и доходит до ревью, то разработка затягивается. Пока это увидят, пока переделают... Да и вообще, это сработает, только если у проверяющего достаточно компетенций / внимательности.
Кстати, хоть
С помощью идентификатора
#cppcore #cpp11
final
для виртуальных методов#новичкам
Продолжаем серию постов! Ранее мы уже упоминали идентификатор со специальным значением
final
в рамках наследования классов — запрещали создавать наследников того или иного класса. Это поведение распространялось на весь класс целиком, в том числе и на все его методы. Это может быть слишком строгим ограничением в какой-то ветке нашего подсемейства. Например, мы хотим его продолжать развивать, но точечно зафиксировать поведение одного конкретного переопределенного метода.Запретить переопределение метода в наследниках можно с помощью идентификатора
final
(С++ 11 и выше):struct Child : public Parent
{
void method_name() override final;
};
Ограничивать переопределения виртуальных методов может показаться неочевидным действием. Казалось бы, вот к чему это? Этот вопрос стоит рассматривать с технической и смысловой точки зрения. Начнем с последнего.
Код, который вы пишите, может быть достаточно глубоко осмыслен и выразителен. В частности, в семействе вашего класса можно выделить подсемейство со специфичным и фиксированным поведением.
В качестве примера, давайте рассмотрим семейство датчиков умного дома. Пусть была разработана открытая библиотека, которая предоставляет некоторый набор интерфейсов для разных типов датчиков. Например, для подсемейства пожарных датчиков. Производители устройств могут наследовать специальный интерфейс и реализовать прошивку для своего девайса. Как у них работает этот сенсор — никто не знает, но главное, что в случае срабатывания такого датчика происходит важное, в рамках этого подсемейства, действие - вызывается бригада пожарных. Это достаточно важный смысл, который может быть заложен в наследника класса и ограничен в переопределении для производителя:
// Датчик возгорания
struct IFireSensor : public ISensor
{
// В случае срабатывания, вызываем пожарных
void onAlarm(control_panel_t *ctrl) override final
{
ctrl->call_fireman();
}
};
Думаю, обосновывать важность этого ограничения не стоит. Если производитель по каким-то причинам захочет вызвать бригаду стриптизёров вместо пожарных, то до жаркой вечеринки дело не дойдёт!
Конечно, можно написать комментарии к коду, в надежде, что их прочитают... Но вот практика показывает, что их периодически игнорируют. Если какое-то ограничение не срабатывает во время компиляции и доходит до ревью, то разработка затягивается. Пока это увидят, пока переделают... Да и вообще, это сработает, только если у проверяющего достаточно компетенций / внимательности.
Кстати, хоть
final
и может быть применён только к виртуальному методу, мы не можем с помощью него проверить действительно ли мы переопределяем метод. Идентификатор final
может быть применён к новому объявленному методу, а override
нельзя. Следовательно, мы можем добиться такой ситуации: живой пример.С помощью идентификатора
final
можно расширить список гарантий, которые предоставляет выделенная ветка семейства классов. Это может оказаться полезным не только для разработчиков, но и для компилятора. Так можно выполнить некоторые оптимизации, ускоряющие работу вашей программы. Поговорим об этом в следующих постах.#cppcore #cpp11