C++ 11 Delegated Constructor Pure Virtual Method & Function Calls — Dangers?

独自空忆成欢 提交于 2019-11-30 11:45:39

With the updates, the example code looks okay to me, with the caveat that if you ever make a subclass of Derived, the subclass's override of Do() won't get called by Derived(const std::string &), rather Derived::Do() will still get called; which might not be what you wanted. In particular, when Initialize() is called from the Derived(const std::string &) constructor, the object is still "only" a Derived object and not a SubDerived object yet (because the SubDerived layer of construction-code hasn't started yet) and that is why Derived::Do() would be called and not SubDerived::Do().

Q: What if the subclass uses the same delegation pattern to ensure everything is instantiate in the same way?

A: That would mostly work, but only if it's okay for Derived::Do() to be called before SubDerived::Do() is called.

In particular, say you had class SubDerived that did the same things as Derived does above. Then when the calling code did this:

SubDerived foo("Hello");

the following sequence of calls would occur:

Base()
Derived()
Derived(const std::string &)
  Base::Initialize()
    Derived::Do()
SubDerived()
SubDerived(const std::string &)
  Base::Initialize()
    SubDerived::Do()

... so yes, SubDerived::Do() would eventually get called, but Derived::Do() would have been called also. Whether or not that will be a problem depends on what the various Do() methods actually do.

Some advice: Calling virtual methods from within a constructor is usually not the best way to go. You might want to consider simply requiring the calling code to call Do() manually on the object after the object is constructed. It's a bit more work for the calling code, but the advantage is that it you can avoid the not-very-obvious-or-convenient semantics that come into play when doing virtual method calls on partially-constructed objects.

In a typical single-constructor inheritance scenario, it is UB to call a pure virtual function in the base constructor:

[C++11: 10.4/6]: Member functions can be called from a constructor (or destructor) of an abstract class; the effect of making a virtual call (10.3) to a pure virtual function directly or indirectly for the object being created (or destroyed) from such a constructor (or destructor) is undefined.

struct Base
{
   Base()
   {
      foo();  // UB
   }

   virtual void foo() = 0;
};

struct Derived : Base
{
   virtual void foo() {}
};

There is no exemption here for such a call being made in a delegated constructor call, because at this point the more-derived part of the object still hasn't been constructed.

struct Base
{
   Base()
   {
      foo();  // still UB
   }

   Base(int) : Base() {};

   virtual void foo() = 0;
};

struct Derived : Base
{
   virtual void foo() {}
};

Here's the Wikipedia passage that you cited:

C++11 considers an object constructed once any constructor finishes execution. Since multiple constructors will be allowed to execute, this will mean that each delegate constructor will be executing on a fully constructed object of its own type. Derived class constructors will execute after all delegation in their base classes is complete.

The key is the second bolded sentence, rather than the first, as one may misconstrue from a quick glance.

However, your posted code snippet is fine, and that's because the derived constructor body is undergoing execution, not the constructor for the abstract class, which has already been completely constructed. That said, the fact that you've had to ask for proof that it's safe should be some indication that this is not the most expressive or intuitive approach, and I would try to avoid it in your design.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!