A less hacky way to call ctor after all superclass ctors and dtor before all superclass dtors in C++

  architecture, c++, inheritance, vtable

I know that before the superclass ctor is finished, the subclass (and vtable) is not yet complete. But, I have a situation where, logically, I must be able to change the initialization of the superclass, and I’m trying to figure out how I can do that in C++.

Here is a simplified example

class Window {
   std::thread uiThread;
   void uiThreadProc(){
       try {
           UIInit();
           NotifyOtherThreadThatUIInitFinished();
       } catch(...){
           UIClean();
           throw; // actually using an exception_ptr but it's irrelevant here
       }

       EventLoop();
       UICleanup();
   }
   virtual void UIInit();
   virtual void UICleanup();
   Super(){
       CreateUIThread();
       WaitForUIThreadToFinishUIInit();
   }
   ~Super(){
       SendQuitEventToUIThread();
       WaitForUIThreadToFinish();
   }
}

class Overlay : public Window {
    Overlay(){
        // can't do stuff before UIInit because Window ctor returns when UIInit finishes
    }
    void UIInit() override {
        // won't be called because UIInit is called before Window ctor returns
    }
    void UIClean() override {
        // won't get called if UIInit throws
    }
    ~Overlay(){}
}

Currently, I’m trying to make it work by making the ctors private and moving ctors to an Init that gets called after ctors like this:

class Window {

   static std::shared_ptr<Window> Create(){
       auto window = std::make_shared<Window>();
       window->Init();
       return window;
   }
   virtual void Init() { /* actual ctor content here */ }
   virtual void Cleanup() { /* actual dtor content here */}
   bool cleanupCalled = false;
   ~Window(){
      if(!cleanupCalled){
          cleanupCalled = true; // with mutex and locks...
          Cleanup(); 
      }
   }
}

class Overlay : public Window {
    // same as above, but now overriding Init() and Cleanup()...
}

This I assume would work but it feels super hacky and convoluted.
And I don’t understand why this is the case from a design perspective, why don’t ctors first create the complete vtable then call the hierarchy of ctors? the vtable doesn’t depend on member initialization so it won’t break things to do it first, right?

Is this not a good use for inheritance? I need to be able to override UIInit because some things must run on the UIthread. I could send it as functions/events and the EventLoop would execute that but that would seriously break the logic of "when ctor finishes, the object is fully initialized" or if it absolutely has to run before the EventLoop. I could make my own thread event handling class, but that simply seems wrong for such a trivial case.

Any architectural suggestions are very welcome. And thank you for reading!

Source: Windows Questions C++

LEAVE A COMMENT