Why is a call to a virtual member function in the constructor a non-virtual call?
Asked Answered
S

15

334

Suppose I have two C++ classes:

class A
{
public:
  A() { fn(); }

  virtual void fn() { _n = 1; }
  int getn() { return _n; }

protected:
  int _n;
};

class B : public A
{
public:
  B() : A() {}

  virtual void fn() { _n = 2; }
};

If I write the following code:

int main()
{
  B b;
  int n = b.getn();
}

One might expect that n is set to 2.

It turns out that n is set to 1. Why?

Saintly answered 7/6, 2009 at 15:46 Comment(4)
I'm asking and answering my own question because I want to get the explanation for this bit of C++ esoterica into Stack Overflow. A version of this issue has struck our development team twice, so I'm guessing this info might be of use to someone out there. Please write out an answer if you can explain it in a different/better way...Saintly
I'm wondering why this got down voted? When I first learned C++ this really confused me. +1Renner
What surprises me is the lack of a compiler warning. The compiler substitutes a call to the “function defined in the class of the current constructor” for what would in any other case be the “most overridden” function in a derived class. If the compiler said “substituting Base::foo() for call to virtual function foo() in constructor” then the programmer would be warned that the code will not do what they expected. That would be a lot more helpful than making a silent substitution, leading to mysterious behavior, lots of debugging, and eventually a trip to stackoverflow for enlightenment.Marotta
@CraigReynolds Not necessarily. There is no need for special compiler treatment of virtual calls inside constructors The base class constructor creates the vtable for the current class only, so at that point the compiler can just call the vitrual function via that vtable in exactly the same way as usual. But the vtable doesn't point to any function in any derived class yet. The vtable for the derived class is adjusted by the derived class's constructor after the base class constructor returns, which is how the override will work once the derived class is constructed.Gormless
L
296

Calling virtual functions from a constructor or destructor is dangerous and should be avoided whenever possible. All C++ implementations should 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. I suggest reading that (and the rest of the FAQ) for a followup.

Excerpt:

[...] In a constructor, the virtual call mechanism is disabled because overriding from derived classes hasn’t yet happened. Objects are constructed from the base up, “base before derived”.

[...]

Destruction is done “derived class before base class”, so virtual functions behave as in constructors: Only the local definitions are used – and no calls are made to overriding functions to avoid touching the (now destroyed) derived class part of the object.

EDIT Corrected Most to All (thanks litb)

Liborio answered 7/6, 2009 at 15:52 Comment(20)
I wish I could upvote this 100 times. Doing such things usually indicates flawed design.Horsecar
Not most C++ implementations, but all C++ implementations have to call the current class's version. If some don't, then those have a bug :). I still agree with you that it's bad to call a virtual function from a base class - but semantics are precisely defined.Maracanda
And the following FAQ page discusses exactly how to work around it.Extension
It's not dangerous, it's just non-virtual. In fact, if methods called from the constructor were called virtually, it would be dangerous because the method could access uninitialized members.Downgrade
Why is calling virtual functions from destructor dangerous? Isn't the object still complete when destructor runs, and only destroyed after the destructor finishes?Cotquean
@Kevin: So what alternative designs should one use in a situation where one may be tempted to do this? (In the case where I considered this, I had the problem of initializing a data field in the base differently depending on the derived type)Skindeep
Your are talking of Calling virtual functions on the same object from a constructor or destructor. This is clear to me, but in your spelling this can lead to misunderstandings.Roswell
This is awfully highly voted for not explaining why and basically amounting to a link-only answer. (and a link to that notorious moving target of the C++ FAQ... well, moving in terms of URLs, if not up-to-date content!)Bourbon
−1 "is dangerous", no, it's dangerous in Java, where downcalls can happen; the C++ rules remove the danger through a pretty expensive mechanism.Hyperborean
In what way is calling a virtual function from a constructor "dangerous"? This is total nonsense.Beet
@SiyuanRen No, the object is not complete anymore. When a destructor runs, derived clases' destructors for the same object have already run and finished. So derived classes' invariants are (potentially) no longer valid, and running a derived-class member would be (potentially) dangerous. The same as with constructors, but in reverse order.Passenger
All C++ implementations should => All conforming C++ implementations will. "Should" is too charitable. The language requires, not recommends, this.Bourbon
Now with C++11, a possible safeguard is to add the "final" keyword to any class that uses virtual methods in its constructor. This prevents from the mentioned problem and legitimate this technique that can be useful in some circumstances. Obviously a side effect is that you can not inherit this class anymore. nb: only tagging as "final" the involved virtual method is not enough for 100% security because this method can still call another, yet undefined, virtual methods.Xantha
@PabloH That is the exact opposite of the truth. When a destructor body runs, the base class's destructor has already run. The derived class's denstructor doesn't run until this constructor has exited.Gormless
@Gormless No, what I wrote is the mandated behaviour. E.g. n3797 draft for C++11 states in 12.4.8 [class.dtor] that "After executing the body of the destructor [...], a destructor for class X calls [...] the destructors for X’s direct base classes [...]. Bases and members are destroyed in the reverse order of the completion of their constructor (see 12.6.2)."Passenger
But how compiler does this technically? Sets vptr to the address of vtable for the current class?Amitie
@Amitie In book c++ data model, the author said the setting of vptr is inserted before the codes written by user, so folling the contruction call, firstly set to the base class's vtable, then the derived class'.Amenity
Can anyone tell me if the calling still uses the vptr(since the vptr is set to the current level) way or just statically calls the version of the current level.Amenity
@BAKEZQ: It’s the vptr one, since it works even if the call occurs in a function called from the constructor/destructor. Direct calls may of course be optimized just like any other devirtualization.Gynous
This accepted answer is helpful but it does not answer the question explicitly.Muskellunge
F
113

Calling a polymorphic function from a constructor is a recipe for disaster in most OO languages. Different languages will perform differently when this situation is encountered.

The basic problem is that in all languages the Base type(s) must be constructed previous to the Derived type. Now, the problem is what does it mean to call a polymorphic method from the constructor. What do you expect it to behave like? There are two approaches: call the method at the Base level (C++ style) or call the polymorphic method on an unconstructed object at the bottom of the hierarchy (Java way).

In C++ the Base class will build its version of the virtual method table prior to entering its own construction. At this point a call to the virtual method will end up calling the Base version of the method or producing a pure virtual method called in case it has no implementation at that level of the hierarchy. After the Base has been fully constructed, the compiler will start building the Derived class, and it will override the method pointers to point to the implementations in the next level of the hierarchy.

class Base {
public:
   Base() { f(); }
   virtual void f() { std::cout << "Base" << std::endl; } 
};
class Derived : public Base
{
public:
   Derived() : Base() {}
   virtual void f() { std::cout << "Derived" << std::endl; }
};
int main() {
   Derived d;
}
// outputs: "Base" as the vtable still points to Base::f() when Base::Base() is run

In Java, the compiler will build the virtual table equivalent at the very first step of construction, prior to entering the Base constructor or Derived constructor. The implications are different (and to my likings more dangerous). If the base class constructor calls a method that is overriden in the derived class the call will actually be handled at the derived level calling a method on an unconstructed object, yielding unexpected results. All attributes of the derived class that are initialized inside the constructor block are yet uninitialized, including 'final' attributes. Elements that have a default value defined at the class level will have that value.

public class Base {
   public Base() { polymorphic(); }
   public void polymorphic() { 
      System.out.println( "Base" );
   }
}
public class Derived extends Base
{
   final int x;
   public Derived( int value ) {
      x = value;
      polymorphic();
   }
   public void polymorphic() {
      System.out.println( "Derived: " + x ); 
   }
   public static void main( String args[] ) {
      Derived d = new Derived( 5 );
   }
}
// outputs: Derived 0
//          Derived 5
// ... so much for final attributes never changing :P

As you see, calling a polymorphic (virtual in C++ terminology) methods is a common source of errors. In C++, at least you have the guarantee that it will never call a method on a yet unconstructed object...

Fug answered 7/6, 2009 at 16:56 Comment(11)
Good job explaining why the alternative is (also) are error-prone.Fencer
"If the base class constructor calls a method that is overriden in the derived class the call will actually be handled at the derived level calling a method on an unconstructed object..." How so if base is already initialized. There is no possiblity unless you explicilty call "init" before initializing other members.Biostatics
An explanation! +1, superior answer imhoBourbon
For me the problem is that there are so many restrictions in C++ classes that its incredible hard to achieve any good design. C++ dictates that "If it could be dangerous forbid it" even if its intuitive causing problems such as: "Why this intuitive behavior doesn't work" to happen all the time.Herdsman
@Herdsman What? C++ does not "forbid" anything in this case. The call is simply treated as a non-virtual call, to the method for the class whose constructor is currently executing. That is a logical consequence of the object construction timeline - not some draconian decision to stop you doing silly things. The fact that it coincidentally fulfills the latter purpose too is just a bonus to me.Bourbon
Yeah, its a logical consequence of the internal implementation. My point is it is unexpected. C++ have too many of these cases and do not protect the user from any (by protection I mean making it hard or impossible to achieve an unexpected result). About "forbid" ok it does not forbid, in most cases, it just crashes which is actually worse. I know it is a language designed a long time ago with complicated efficiency constraints but it is still a design problem. Checkout rust programming language for a counter example.Herdsman
@Herdsman It's only "unexpected" to people who don't bother to actually learn how the language works before using it, and instead just assume their expectations will reflect reality without checking - in which case, I have no sympathy: languages, by definition, are something that you learn, not "expect". I contend that, as a design decision, it is clearly documented and makes perfect sense. And I don't want to use another language, though you didn't explain why it makes a relevant counterexample anyway.Bourbon
@Bourbon It's not treated as a non-virtual call. It's treated the same as any other virtual call. The point is that within the constructor the vtable doesn't point to any derived class's overridden methods yet, so the virtual call can't 'see' beyond the current class scope.Gormless
You are completely wrong how Java does it.Vocabulary
Very well explained. So, I wish my compiler warned me about that... :(Maemaeander
@Enerccio: #3404801 affirms that the described Java behavior is accurate.Lemar
S
83

The reason is that C++ objects are constructed like onions, from the inside out. Base classes are constructed before derived classes. So, before a B can be made, an A must be made. When A's constructor is called, it's not a B yet, so the virtual function table still has the entry for A's copy of fn().

Saintly answered 7/6, 2009 at 15:46 Comment(6)
C++ does not normally use the term "super class" - it prefers "base class".Radioactivate
That is the same in most OO languages: you cannot possibly build a derived object without the base part being already constructed.Juanitajuanne
@DavidRodríguez-dribeas other languages do actually do that. For example in Pascal, memory is allocated for the whole object first, but then only the most-derived constructor is invoked. A constructor must either contain an explicit call to its parent's constructor (which does not have to be the first action - it just has to be somewhere), or if it doesn't , it's as if the first line of the constructor made that call.Phocaea
Thanks for the clarity and avoidance of details which doesnt go straight to the outcomeCalen
If the calling still uses the vptr(since the vptr is set to the current level as you metioned too) way or just statically calls the version of the current level.Amenity
When programmers don't know onions are not made by construction :DDRacecourse
P
33

The C++ FAQ Lite Covers this pretty well:

Essentially, during the call to the base classes constructor, the object is not yet of the derived type and thus the base type's implementation of the virtual function is called and not the derived type's.

Photochronograph answered 7/6, 2009 at 16:3 Comment(8)
Clear, straightforward, simplest answer. It's still a feature I would love to see get some love. I hate having to write all these silly initializeObject() functions that the user is forced to call right after the construction, just bad form for a very common use case. I understand the difficulty though. C'est la vie.Weeds
@Weeds What "love" do you propose? Bear in mind that you can't just change how things currently work in-place, because that would horribly break reams of existing code. So, how would you do it instead? Not only what new syntax you would introduce to allow (actual, non-devirtualised) virtual calls in constructors - but also how you would somehow amend the models of object construction/lifetime so that those calls would have a complete object of the derived type on which to run. This'll be interesting.Bourbon
@Bourbon I don't think any syntax changes would be needed. Maybe when creating an object, the compiler would add code to walk the vtable and look for this case and patch things then? I've never written a C++ compiler and I'm quite sure my initial comment to give this some "love" was naive and this will never happen. :-) A virtual initialize() function isn't a very painful workaround anyway, you just have to remember to call it after creating your object.Weeds
@Bourbon I just noticed your other comment below, explaining that the vtable isn't available in constructor, emphasizing again the difficulty here.Weeds
@Weeds Syntax changes are needed to avoid breaking existing code that assumes, per all Standards to date, that a ctor calling a virtual method calls the constructing class's override, not the most derived one. Such calls aren't desirable, so we all try to avoid them & wince when we end up (ideally temporarily) using one. But they get into code. Such code expects that behaviour, & if the language starts calling the most-derived override instead, the code breaks (certainly in theory, very possibly in practice). So, even if possible, this cannot be adopted without explicit, different syntax.Bourbon
@Weeds I goofed when writing about the vtable not being available in the constructor. It is available, but the constructor only sees the vtable for its own class, because each derived constructor updates the instance's vptr to point at the vtable for the current derived type and no further. So, the current ctor sees a vtable that only has its own overrides, hence why it can't call any more-derived implementations of any virtual functions.Bourbon
@Bourbon makes sense, thanks. And regarding standards changes, totally agree. Any standards change has to consider the massive existing codebase, and this concept would be disruptive.Weeds
Just for fun... Maybe you could add three colons base:::function() to get super-virtualized behavior? :-)Weeds
P
20

One solution to your problem is using factory methods to create your object.

  • Define a common base class for your class hierarchy containing a virtual method afterConstruction():
class Object
{
public:
  virtual void afterConstruction() {}
  // ...
};
  • Define a factory method:
template< class C >
C* factoryNew()
{
  C* pObject = new C();
  pObject->afterConstruction();

  return pObject;
}
  • Use it like this:
class MyClass : public Object 
{
public:
  virtual void afterConstruction()
  {
    // do something.
  }
  // ...
};

MyClass* pMyObject = factoryNew();

Psalmist answered 7/6, 2009 at 17:9 Comment(0)
P
5

As has been pointed out, the objects are created base-down upon construction. When the base object is being constructed, the derived object does not exist yet, so a virtual function override cannot work.

However, this can be solved with polymorphic getters that use static polymorphism instead of virtual functions if your getters return constants, or otherwise can be expressed in a static member function, This example uses CRTP (https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern).

template<typename DerivedClass>
class Base
{
public:
    inline Base() :
    foo(DerivedClass::getFoo())
    {}

    inline int fooSq() {
        return foo * foo;
    }

    const int foo;
};

class A : public Base<A>
{
public:
    inline static int getFoo() { return 1; }
};

class B : public Base<B>
{
public:
    inline static int getFoo() { return 2; }
};

class C : public Base<C>
{
public:
    inline static int getFoo() { return 3; }
};

int main()
{
    A a;
    B b;
    C c;

    std::cout << a.fooSq() << ", " << b.fooSq() << ", " << c.fooSq() << std::endl;

    return 0;
}

With the use of static polymorphism, the base class knows which class' getter to call as the information is provided at compile-time.

Pufahl answered 11/4, 2018 at 3:30 Comment(2)
I think I will avoid to do this. This is not single Base class any more. You actually created lots of different Base class.Hurwitz
@Hurwitz Exactly: Base<T> is just a helper class, not a common interface type that can be used for runtime polymorphism (f.ex. heterogeneous containers). These are useful too, just not for the same tasks. Some classes inherit both from a base class that's an interface type for runtime polymorphism and another that's a compile time template helper.Centralia
H
3

The C++ Standard (ISO/IEC 14882-2014) say's:

Member functions, including virtual functions (10.3), can be called during construction or destruction (12.6.2). When a virtual function is called directly or indirectly from a constructor or from a destructor, including during the construction or destruction of the class’s non-static data members, and the object to which the call applies is the object (call it x) under construction or destruction, 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. If the virtual function call uses an explicit class member access (5.2.5) and the object expression refers to the complete object of x or one of that object’s base class subobjects but not x or one of its base class subobjects, the behavior is undefined.

So, Don't invoke virtual functions from constructors or destructors that attempts to call into the object under construction or destruction, Because the order of construction starts from base to derived and the order of destructors starts from derived to base class.

So, attempting to call a derived class function from a base class under construction is dangerous.Similarly, an object is destroyed in reverse order from construction, so attempting to call a function in a more derived class from a destructor may access resources that have already been released.

Hedvig answered 23/8, 2017 at 7:3 Comment(0)
F
2

Do you know the crash error from Windows explorer?! "Pure virtual function call ..."
Same problem ...

class AbstractClass 
{
public:
    AbstractClass( ){
        //if you call pureVitualFunction I will crash...
    }
    virtual void pureVitualFunction() = 0;
};

Because there is no implemetation for the function pureVitualFunction() and the function is called in the constructor the program will crash.

Fulmar answered 7/6, 2009 at 16:51 Comment(4)
It's hard to see how this is the same problem, as you didn't explain why. Calls to non-pure virtual functions during ctors are perfectly legal, but they just don't go through the (not yet constructed) virtual table, so the version of the method that gets executed is the one defined for the class type whose ctor we are in. So those don't crash. This one does because it's pure virtual and unimplemented (side note: one can implement pure virtual functions in the base), so there is no version of the method to be called for this class type, & the compiler assumes you don't write bad code, so boomBourbon
D'oh. The calls do go through the vtable, but it hasn't yet been updated to point at the overrides for the most-derived class: only the one being constructed right now. Still, the result and reason for the crash remains the same.Bourbon
@Bourbon "(side note: one can implement pure virtual functions in the base)" No you can not, otherwise the method is no longer pure virtual. You also can not make instances of an abstract class, so the example by TimW will not compile if you try to call a pure method from the constructor. It now compiles because the constructor does not call pure virtual methods and contains no code, just a comment.Madder
@user2943111: Yes, you CAN. Pure virtual in C++ means that the class is abstract and every derived class will also be abstract unless it overrides the pure virtual member function. A virtual member function without any definition must be pure, but the reverse is not true.Stakhanovism
B
1

The vtables are created by the compiler. A class object has a pointer to its vtable. When it starts life, that vtable pointer points to the vtable of the base class. At the end of the constructor code, the compiler generates code to re-point the vtable pointer to the actual vtable for the class. This ensures that constructor code that calls virtual functions calls the base class implementations of those functions, not the override in the class.

Benefactor answered 7/5, 2013 at 10:14 Comment(6)
The vptr isn't changed at the end of the ctor. In the body of ctor C::C, virtual function calls go the C overrider, not to any base class version.Centralia
The dynamic type of the object is defined after the ctor has called base class ctors and before it constructs its members. So the vptr isn't changed at the end of the ctor.Centralia
@Centralia I am saying the same thing, that vptr is not changed at the end of constructor of base class, it will be changed at the end of constructor of derived class. I hope you are telling the same. Its an compiler/implementation dependent thing. When are you proposing that vptr should change. Any good reason for downvoting?Benefactor
The timing of the change of vptr is not implementation dependent. It is prescribed by language semantics: the vptr changes when the dynamic behavior of the class instance changes. There is no freedom here. Inside the body of a ctor T::T(params), the dynamic type is T. The vptr will reflect that: it will point to vtable for T. Do you disagree?Centralia
Maybe it would easier to have a real example of inheritance to talk aboutCentralia
You can track how it changes as I did above.Facile
S
0

Firstly,Object is created and then we assign it 's address to pointers.Constructors are called at the time of object creation and used to initializ the value of data members. Pointer to object comes into scenario after object creation. Thats why, C++ do not allows us to make constructors as virtual . .another reason is that, There is nothing like pointer to constructor , which can point to virtual constructor,because one of the property of virtual function is that it can be used by pointers only.

  1. Virtual functions are used to assign value dynamically,as constructors are static,so we can not make them virtual.
Sudduth answered 10/11, 2017 at 0:48 Comment(0)
D
0

As a supplement, calling a virtual function of an object that has not yet completed construction will face the same problem.

For example, start a new thread in the constructor of an object, and pass the object to the new thread, if the new thread calling the virtual function of that object before the object completed construction will cause unexpected result.

For example:

#include <thread>
#include <string>
#include <iostream>
#include <chrono>

class Base
{
public:
  Base()
  {
    std::thread worker([this] {
      // This will print "Base" rather than "Sub".
      this->Print();
    });
    worker.detach();
    // Try comment out this code to see different output.
    std::this_thread::sleep_for(std::chrono::seconds(1));
  }
  virtual void Print()
  {
    std::cout << "Base" << std::endl;
  }
};

class Sub : public Base
{
public:
  void Print() override
  {
    std::cout << "Sub" << std::endl;
  }
};

int main()
{
  Sub sub;
  sub.Print();
  getchar();
  return 0;
}

This will output:

Base
Sub
Delsiedelsman answered 29/1, 2021 at 3:36 Comment(2)
Hello and welcome to SO! Please read the tour, and How do I write a good answer? For example adding a code snippet might help.Collectivize
This solution has undefined behavior. sleep_for does not synchronize threads, so you have a race on this->Print() both during construction and destruction. Second, this risks crashing as the worker requires this to still exist (it is a member function) but there is no guarantee of that. If you don't have an arbitrary wait like getchar() the Sub instance can easily reach the end of its lifetime before the thread prints. Solutions that rely on detach() are almost always broken.Ripieno
F
0

To answer what happens/why when you run that code, I compiled it via g++ -ggdb main.cc, and stepped through with gdb.

main.cc:

class A { 
  public:
    A() {
      fn();
    }
    virtual void fn() { _n=1; }
    int getn() { return _n; }

  protected:
    int _n;
};


class B: public A {
  public:
    B() {
      // fn();
    }
    void fn() override {
      _n = 2;
    }
};


int main() {
  B b;
}

Setting a break point at main, then stepping into B(), printing the this ptr, taking a step into A() (base constructor):

(gdb) step
B::B (this=0x7fffffffde80) at main2.cc:16
16    B() {
(gdb) p this
$27 = (B * const) 0x7fffffffde80
(gdb) p *this
$28 = {<A> = {_vptr.A = 0x7fffffffdf80, _n = 0}, <No data fields>}
(gdb) s
A::A (this=0x7fffffffde80) at main2.cc:3
3     A() {
(gdb) p this
$29 = (A * const) 0x7fffffffde80

shows that this initially points at the derived B obj b being constructed on the stack at 0x7fffffffde80. The next step is into the base A() ctor and this becomes A * const to the same address, which makes sense as the base A is right in the start of B object. but it still hasn't been constructed:

(gdb) p *this
$30 = {_vptr.A = 0x7fffffffdf80, _n = 0}

One more step:

(gdb) s
4       fn();
(gdb) p *this
$31 = {_vptr.A = 0x402038 <vtable for A+16>, _n = 0}

_n has been initialized, and it's virtual function table pointer contains the address of virtual void A::fn():

(gdb) p fn
$32 = {void (A * const)} 0x40114a <A::fn()>
(gdb) x/1a 0x402038
0x402038 <_ZTV1A+16>:   0x40114a <_ZN1A2fnEv>

So it makes perfect sense that the next step executes A::fn() via this->fn() given the active this and _vptr.A. Another step and we're back in B() ctor:

(gdb) s
B::B (this=0x7fffffffde80) at main2.cc:18
18    }
(gdb) p this
$34 = (B * const) 0x7fffffffde80
(gdb) p *this
$35 = {<A> = {_vptr.A = 0x402020 <vtable for B+16>, _n = 1}, <No data     fields>}

The base A has been constructed. Note that address stored in the virtual function table pointer has changed to the vtable for derived class B. And so a call to fn() would select the derived class override B::fn() via this->fn() given the active this and _vptr.A (un-comment call to B::fn() in B() to see this.) Again examining 1 address stored in _vptr.A shows it now points to the derived class override:

(gdb) p fn
$36 = {void (B * const)} 0x401188 <B::fn()>
(gdb) x/1a 0x402020
0x402020 <_ZTV1B+16>:   0x401188 <_ZN1B2fnEv>

By looking at this example, and by looking at one with a 3 level inheritance, it appears that as the compiler descends to construct the base sub-objects, the type of this* and the corresponding address in _vptr.A change to reflect the current sub-object being constructed, - so it gets left pointing to the most derived type's. So we would expect virtual functions called from within ctors to choose the function for that level, i.e., same result as if they were non-virtual.. Likewise for dtors but in reverse. And this becomes a ptr to member while members are being constructed so they also properly call any virtual functions that are defined for them.

Facile answered 28/9, 2021 at 21:38 Comment(0)
J
-1

I just had this error in a program. And I had this thinking : what happens if the method is marked as pure virtual in the constructor?

class Base {
public:
    virtual int getInt() = 0;
    
    Base(){
        printf("int=%d\n", getInt());
    }
};

class Derived : public Base {
    public:
        virtual int getInt() override {return 1;}
};

And... funny thing! You first get a warining by the compiler :

warning: pure virtual ‘virtual int Base::getInt() const’ called from constructor

And an error from ld!

/usr/bin/ld: /tmp/ccsaJnuH.o: in function `Base::Base()':
main.cpp:(.text._ZN4BaseC2Ev[_ZN4BaseC5Ev]+0x26): undefined reference to `Base::getInt()'
collect2: error: ld returned 1 exit status

This is totally illogic that you get just a warning from the compiler!

Jamajamaal answered 28/12, 2022 at 17:19 Comment(0)
E
-2

I am not seeing the importance of the virtual key word here. b is a static-typed variable, and its type is determined by compiler at compile time. The function calls would not reference the vtable. When b is constructed, its parent class's constructor is called, which is why the value of _n is set to 1.

Epinasty answered 28/10, 2013 at 6:58 Comment(6)
The question is why b's constructor calls the base f(), not the derived override of it. Type of the variable b is irrelevant to that.Bourbon
"The function calls would not reference the vtable" That is not true. If you think virtual dispatch is only enabled when accessing through a B* or ` B&`, you are mistaken.Beet
Aside from the fact that it follows its own logic to the wrong conclusion... The idea behind this answer, known static type, is misapplied. A compiler could devirtualise b.getN() because it knows the real type, & just directly dispatch to the version from B. But that's just an allowance made by the as-if rule. Everything still must act as-if the virtual table is used & followed to the letter. In the A constructor, the same is true: even if (probably not possible) it gets inlined w/ the B ctor, the virtual call must still act as-if it only has the base A vtable available to use.Bourbon
@LightnessRacesinOrbit Can you give me an example for your assertion that virtual dispatch happens without calling through a reference or pointer (including the implicit this)?Pellegrino
@Epinasty You are right that the call b.getn() is non-virtual. b is a statically typed object, and whatever getn() is defined for its type will be called. But inside member functions, including the constructor, all member function calls are made through the implicit this pointer and are hence virtual function calls, if it is a polymorphic class. The reason and rationale for resolving the virtual fn() call to the base class's implementation -- even though it happens during the overall construction of a derived object -- is explained in the other answers.Pellegrino
@PeterA.Schneider "including the implicit this" That's the case I seemed to believe the author was missingBeet
C
-4

During the object's constructor call the virtual function pointer table is not completely built. Doing this will usually not give you the behavior you expect. Calling a virtual function in this situation may work but is not guaranteed and should be avoided to be portable and follow the C++ standard.

Complementary answered 7/6, 2009 at 17:0 Comment(2)
"Calling a virtual function in this situation may work but is not guaranteed" That is not correct. The behaviour is guaranteed.Centralia
@Centralia ...guaranteed to call the base version if available, or to invoke UB if the vfunc is pure virtual.Bourbon

© 2022 - 2024 — McMap. All rights reserved.