Это плохая практика для вызова виртуальной функции из конструктора класса, который помечен как финальный

Обычно вызов виртуальных функций из конструкторов считается плохой практикой, поскольку переопределенные функции в подобъектах не будут вызываться, поскольку объекты еще не созданы.

Но рассмотрим следующие классы:

class base
{
public:
base() {}
~base() {}

private:
virtual void startFSM() = 0;
};

class derived final : public base
, public fsm_action_interface
{
public:
derived() : base{}
, theFSM_{}
{  startFSM(); }/// FSM interface actions

private:
virtual void startFSM()
{ theFSM_.start(); }

private:
SomeFSMType theFSM_;
}

В этом случае класс derived помечен как final поэтому никакие дополнительные суб-объекты не могут существовать. Поэтому виртуальный вызов будет разрешен правильно (для наиболее производного типа).

Это все еще считается плохой практикой?

17

Решение

относительно

» Обычно вызов виртуальных функций из конструкторов считается плохой практикой, поскольку переопределенные функции в подобъектах не будут вызываться, поскольку объекты еще не созданы.

Это не относится к делу. Среди компетентных программистов C ++ обычно не считается плохой практикой вызывать виртуальные функции (кроме чисто виртуальных) из конструкторов, потому что C ++ предназначенный чтобы справиться с этим хорошо. В отличие от таких языков, как Java и C #, где это может привести к вызову метода для еще неинициализированного подобъекта производного класса.

Обратите внимание, что динамическая настройка динамического типа требует затрат времени выполнения.

В языке, ориентированном на предельную эффективность, в качестве основного руководящего принципа «вы не платите за то, что вы не используете», это означает, что это важная и очень намеренная функция, а не произвольный выбор. Это там только для одной цели. А именно для поддержки этих звонков.


относительно

» В этом случае производный класс помечается как окончательный, поэтому дальнейшие подобъекты не могут существовать. Поэтому виртуальный вызов будет разрешен правильно (для наиболее производного типа).

Стандарт C ++ гарантирует, что во время выполнения конструкции для класса T, динамический тип T.

Таким образом, не было проблем с разрешением неправильного типа, во-первых.


относительно

» Это все еще считается плохой практикой?

Это действительно плохая практика объявлять функцию-член virtual в final класс, потому что это бессмысленно. «Все еще» не очень значимо или.

Извините, я не видел, чтобы функция виртуального члена была унаследована как таковая.

Рекомендуется использовать ключевое слово для обозначения функции-члена как переопределения или реализации чисто виртуального. override, не отмечать это как virtual,

Таким образом:

void startFSM() override
{ theFSM_.start(); }

Это гарантирует ошибку компиляции, если она не переопределение / реализация.

8

Другие решения

Это все равно будет считаться плохой практикой, так как это почти всегда указывает на плохой дизайн. Вы должны были бы прокомментировать этот код, чтобы объяснить, почему это работает в этом одном случае.

Приведенный выше комментарий Т.С. подтверждает одну из причин, почему это считается плохой практикой.

Что произойдет, если через год вы решите, что
не должно быть окончательным в конце концов?

Тем не менее, в приведенном выше примере шаблон будет работать без проблем. Это потому, что конструктор наиболее производного типа — это тот, который вызывает виртуальную функцию. Эта проблема проявляется, когда конструктор базового класса вызывает виртуальную функцию, которая разрешает реализацию подтипа. В C ++ такая функция не вызывается, потому что во время конструирования базового класса такие вызовы никогда не перейдут к более производному классу, чем тот, который выполняется в данный момент выполняемым конструктором или деструктором. По сути, вы получаете поведение, которого не ожидали.

Редактировать:

Все (правильные / не ошибочные) реализации C ++ должны вызывать версию функции, определенной на уровне иерархии в текущем конструкторе, и не более того.

C ++ FAQ Lite это довольно подробно описано в разделе 23.7.

Скотт Мейерс также взвешивает общую проблему вызова виртуальных функций из конструкторов и деструкторов в Эффективный C ++ Пункт 9

9

Это может работать, но почему startFSM() нужно быть virtual? Вы ни в коем случае не хотите звонить derived::startFSM()так зачем вообще динамическое связывание? Если вы хотите, чтобы он вызывал то же самое, что и динамически связанный метод, создайте другую не виртуальную функцию с именем startFSM_impl() и иметь как конструктор, так и startFSM() позвони вместо этого.

Всегда предпочитайте не виртуальные виртуальные, если вы можете помочь.

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector