简体   繁体   中英

Is it bad practice to call a virtual function from constructor of a class that is marked final

Normally calling virtual functions from constructors is considered bad practice, because overridden functions in sub-objects will not be called as the objects have not been constructed yet.

But, Consider the following classes:

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_;
}

In this case class derived is marked as final so no o further sub-objects can exist. Ergo the virtual call will resolve correctly (to the most derived type).

Is it still considered bad practice?

This would still be considered bad practice as this sort of this almost always indicates bad design. You'd have to comment the heck out of the code to explain why this works in this one case.

TC's comment above reinforces one of the reasons why this is considered bad practice.

What happens if, a year down the line, you decide that derived shouldn't be final after all?

That said, in the example above, the pattern will work without issue. This is because the constructor of the most derived type is the one calling the virtual function. This problem manifests itself when a base class's constructor calls a virtual function that resolves to a subtype's implementation. In C++, such a function won't get called, because during base class construction, such calls will never go to a more derived class than that of the currently executing constructor or destructor. In essence, you end up with behavior you didn't expect.

Edit:

All (correct/non-buggy) C++ implementations have to call the version of the function defined at the level of the hierarchy in the current constructor and no further.

The C++ FAQ Lite covers this in section 23.7 in pretty good detail.

Scott Meyers also weighs in on the general issue of calling virtual functions from constructors and destructors in Effective C++ Item 9

Regarding

Normally calling virtual functions from constructors is considered bad practice, because overridden functions in sub-objects will not be called as the objects have not been constructed yet.

That is not the case. Among competent C++ programmers it's normally not regarded as bad practice to call virtual functions (except pure virtual ones) from constructors, because C++ is designed to handle that well. In contrast to languages like Java and C#, where it might result in a call to a method on an as yet uninitialized derived class sub-object.

Note that the dynamic adjustment of dynamic type has a runtime cost.

In a language oriented towards ultimate efficiency, with "you don't pay for what you don't use" as a main guiding principle, that means that it's an important and very much intentional feature, not an arbitrary choice. It's there for one purpose only. Namely to support those calls.


Regarding

In this case class derived is marked as final so no o further sub-objects can exist. Ergo the virtual call will resolve correctly (to the most derived type).

The C++ standard guarantees that at the time of construction execution for a class T , the dynamic type is T .

Thus there was no problem about resolving to incorrect type, in the first place.


Regarding

Is it still considered bad practice?

It is indeed bad practice to declare a member function virtual in a final class, because that's meaningless. The “still” is not very meaningful either.

Sorry, I didn't see that the virtual member function was inherited as such.

Best practice for marking a member function as an override or implementation of pure virtual, is to use the keyword override , not to mark it as virtual .

Thus:

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

This ensures a compilation error if it is not an override/implementation.

It can work, but why does startFSM() need to be virtual ? In no case do you actually want to actually call anything but derived::startFSM() , so why have any dynamic binding at all? If you want to have it call the same thing as a dynamically binded method, make another non-virtual function called startFSM_impl() and have both the constructor and startFSM() call it instead.

Always prefer non-virtual to virtual if you can help it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM