Подтвердить что ты не робот

Неправильная практика - вызывать виртуальную функцию из конструктора класса, который помечен как final

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

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

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, поэтому никаких дополнительных под-объектов не существует. Ergo виртуальный вызов будет корректно разрешен (для самого производного типа).

До сих пор считается плохой практикой?

4b9b3361

Ответ 1

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

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

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

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

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


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

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

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

Таким образом, в первую очередь не было проблемы с решением неправильного типа.


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

" До сих пор считается плохой практикой?

Действительно, плохой практикой является объявление функции-члена virtual в классе final, потому что это не имеет смысла. "По-прежнему" не очень значимо .дел >

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

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

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

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

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

Ответ 2

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

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

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

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

Edit:

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

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

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

Ответ 3

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

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