5

In C++ in a Base constructor, the actual type of 'this' is 'Base' not 'Derived', so it's unsafe to call virtual functions. However, is it safe for the base class to pass the value of 'this' during base construction to a callback object that will call a virtual function at a future time after construction is complete?

Dennis
  • 2,607
  • 3
  • 21
  • 28
  • This question seems under-specified. "it's unsafe to call virtual functions" doesn't seem specific enough, and you haven't explained what you mean by "safe." – Robert Harvey Oct 18 '16 at 15:35
  • 5
    It's perfectly safe to call virtual functions as long as you know what the call does. – T.C. Oct 18 '16 at 15:36
  • I guess what I'm really asking is does it avoid invoking undefined behavior, but I'm not sure the best way to phrase that for the question. – Dennis Oct 18 '16 at 15:38

3 Answers3

4

In constructor it is not unsafe to call virtual methods because of this being of type Base but because the object is not fully constructed yet. Derived members are not initialized at this point, so executing the virtual method implementation in Derived would operate on uninitialized instances.

This is why standard specifies that during constructor/destructor call the function called is the final overrider in the constructor’s or destructor’s class and not one overriding it in a more-derived class

You can safely store it and call virtual functions on it from the moment it is fully constructed, i.e. right after the constructor exits (those virtual functions which are defined on Base class, of course).

Zdeslav Vojkovic
  • 14,391
  • 32
  • 45
  • Actually calling virtual functions from construtor will not invoke children methods. Instead methods of current class will be invoked. Vtable will point to vtable of current class and will be switched at the beginning of child constructor. – Alexey Guseynov Oct 18 '16 at 15:43
  • I know that it typically works that way, but AFAIK it is an implementation detail and not a part of specification. I might be wrong. – Zdeslav Vojkovic Oct 18 '16 at 15:45
  • just checked the spec, and I was wrong. It is defined. Fixing the answer. – Zdeslav Vojkovic Oct 18 '16 at 15:49
1

Yes, it is safe. The object will not be moved around in the memory. Realistically, vtable ptr will be updated, but it'll still be accessible from the unchanged this pointer you have saved.

krzaq
  • 16,240
  • 4
  • 46
  • 61
  • You might want to edit your answer so that it's clear you are not referring to move construction but rather that the object will not be moved in RAM. – Dennis Oct 18 '16 at 15:37
  • What will happen if in the constructor you save the `this`pointer for the callback, but an exception is raised before the end of the construction ? – Christophe Oct 18 '16 at 18:24
  • @Christophe The same thing that would happen if your object's lifetime ended and the callback was called later. UB. – krzaq Oct 18 '16 at 18:26
0

It is safe to store the this pointer in a base class's constructor for a later use, but only under the following conditions:

  • Implement the rule of 3 or the rule of 5 if the pointer is stored within the object.
  • Ensure that moving the object (i.e. move construction or move assignment) will update the stored address. For example, your object could be created in a vector: the vector growth might causes the object to be relocated/moved before the callback is called.
  • Ensure that destroying the object would clear the stored address, to prevent the call-back function of using a dangling pointer.
  • Store the pointer in the constructor after any statement that might cause the construction to fail (for example due to an exception). Failed constructions could be very nasty for your callback, because an exception in a constructor would cause the object not to exist (i.e. call back should not be called) and therefore the destructor would not be called (see more details here and here).

So it's safe to do so, but it's not as easy as it may appears: extreme care is required.

Community
  • 1
  • 1
Christophe
  • 68,716
  • 7
  • 72
  • 138