What happens when an asynchronous callback calls a virtual function, while base class constructor is not returned yet?

StackOverflow https://stackoverflow.com/questions/23273896

Question

I have the following scenario:

class Caller{
public:
  Caller()               {...}
  void register(Base* b) {...}
  void callBase() { b->virt()}
};

class Base {
public:
  Base(Caller c)      { println("Base::Base()");  c.register(this); sleep(30); }
  virtual void virt() { println("Base::virt()"); }
};

class Derived : public Base {
public:
  Derived()           { println("Derived::Derived()"); }
  virtual void virt() { println("Derived::virt()"); }
};

I know normally if someone calls virt on the derived class Derived::virt() will be called. But here, if the function callBase is called while Base is sleeping in the parent constructor, which function will be called? Base::virt() or Derived::virt()?

Thanks

Was it helpful?

Solution 2

According to the C++ Lite FAQ 23.5, Base::virt() will be called.

Anyway, it's really not something you want to do - if your object is used by another thread without proper initialization, you can encounter all sorts of nasty race conditions. For example, what would happen if the second thread called virt exactly when the vtable is set? Who's to guarantee that setting the vtable during object construction is an atomic operation?

You should design your code in such a way that your objects aren't used before they're fully initialized.

OTHER TIPS

It would call Base::virt. Until the base class constructor has finished, virtual functions are dispatched as if the dynamic type were Base.

(Although technically you'd have undefined behaviour if another thread accessed the object without proper synchronisation. And the program wouldn't compile since register is a keyword.)

While in the Constructor or Destructor for Base, the class acts in nearly every way as if it was a Base.
That means the Base-implementation for any called virtual function is used.

If there is none (which is possible, because Base may declare it as pure virtual, making Basean abstract class), you get Undefined Behavior, so everything can happen.
Often seen behaviors for this case include crashing on invalid access, deliberately aborting with an error and calling some derived classes override.

If the call is concurrently (multithreading/signals), the standard says you go straight to UB.

Java, C# and the like have completely different rules there, so examine this for each new language in isolation.

Aside: Using register as a name in C++ is ill-advised. It is not allowed, because it is a defunct (i.e. no effect at all) storage-class keyword.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top