4

Possible Duplicate:
C++ virtual function from constructor
Calling virtual functions inside constructors

This question was asked in interview .

I guess I had answered the 1st part correctly but not sure about the 2nd part. In fact I have no clue about 2nd part.

  1. What output does the following code generate? Why?
  2. What output does it generate if you make A::Foo() a pure virtual function?

When I tried running same question on my compiler with virtual void foo() = 0; it throws error "undefined reference to `A::Foo()'"

#include <iostream>

using namespace std;

class A     
{    
public:       
    A()             
    {
        this->Foo();
    }
    virtual void Foo() 
    {
        cout << "A::Foo()" << endl;
    }
};

class B : public A      
{     
public:     
    B()      
    {
        this->Foo();      
    }
    virtual void Foo() 
    {
        cout << "B::Foo()" << endl;
    }
};

int main(int, char**)
{
    B   objectB;
    return 0;
}
Community
  • 1
  • 1
samantha
  • 565
  • 3
  • 13
  • 29
  • 5
    So... what is your question now? – Kerrek SB Nov 28 '11 at 22:37
  • 1. Ans for first question is : A::Foo() B::Foo() 2. Ans for sec question is : it depends Output will be same if you change the class A to : class A { public: A() { this->Foo(); } virtual void Foo() = 0 { cout << "A::Foo()" << endl; } }; And there will be compile error if you change the class to be : class A { public: A() { this->Foo(); } virtual void Foo() = 0; }; – lsrawat Jul 15 '19 at 08:41

4 Answers4

9

When you instantiate a B object, the following happens:

  1. B's constructor is called.

  2. First thing, B's constructor calls the base constructor A().

  3. Inside A's constructor, the function call is dispatched to A::foo(), since this has static and dynamic type A* (nothing else makes sense if you think about it); now the A subobject is complete.

  4. Now B's constructor body runs. Here the function call is dispatched to B::foo(). Now the entire B object is complete.

If A::foo() is pure-virtual, step (3) causes undefined behaviour; cf. 10.6/4 in the standard.

(In your case possibly manifesting as a linker error, since the compiler optimizes to resolve the call statically, and the symbol A::foo is not found.)

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • 1. The "dispatched statically" explanations are a bit misleading. This is not guaranteed. It is an optimization that the compiler can make if it can prove which implementation will be executed, but it can make this optimization for any call of virtual, not just calls from a constructor. – Cheers and hth. - Alf Nov 28 '11 at 22:50
  • The *pure virtual* case is wrong. There will be no linker error, but rather undefined behavior, and in this particular case (quality of implementation) all compilers I know of will print a message and call `terminate()`. The static dispatch... it need not be static dispatch, it can use dynamic dispatch, but the important bit is that the type of the object being constructed is `A` while `A` constructor is being executed (consider that the constructor could call a non-virtual function that would dispatch to a virtual function, dynamic dispatch would be used, and still `A::foo()` would be called. – David Rodríguez - dribeas Nov 28 '11 at 22:50
  • @AlfP.Steinbach: How do you mean? Inside the base constructor, `this` has static *and* dynamic type `A*`. Ah, OK, you mean that it might still be *dynamically* dispatched to `A::foo()`... OK, I will clarify that. – Kerrek SB Nov 28 '11 at 22:53
  • Step (3) won't necessarily give a linker error if `A::foo()` is pure virtual - I've encountered compilers that will call the function anyway if it is defined, and others that fail at run-time if it isn't defined. All you can say is that calling a pure virtual function from a constructor or destructor gives undefined behaviour. – Mike Seymour Nov 28 '11 at 22:54
  • @MikeSeymour: Yep, already fixed. (Still waiting for a reference, though.) – Kerrek SB Nov 28 '11 at 22:54
  • 2. Contrary to the claim at the end, there is not necessarily a linking error. When the pure virtual is called via some other function the compiler is not necessarily smart enough to recognize the virtual call at compile time. In this case a reasonable quality compiler will have ensured that you get a runtime error. In the case where the pure virtual has a definition but is called directly from the constructor, both Visual C++ 10.0 and g++ 4.4.1 just generate a call of the definition (no crash). That is one allowed effect of Undefined Behavior, that it does what someone expected. – Cheers and hth. - Alf Nov 28 '11 at 22:58
  • @KerrekSB: if you want a reference, 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." – Mike Seymour Nov 28 '11 at 23:06
  • @MikeSeymour: Thanks, I shall add this! – Kerrek SB Nov 28 '11 at 23:06
5

In the second case you have undefined behavior (calling a pure virtual of class T in a class T constructor), so the output could be anything – if it even compiles.

The main thing to understand is that in C++, the dynamic type of an object is T when an object's T constructor executes.

This makes it safe to call virtual functions from a C++ constructor. You don't get a call down into an uninitialized derived class sub-object. In contrast, in Java and C# (and similar languages) you can easily get that kind of bug, and it's common.

halfer
  • 19,824
  • 17
  • 99
  • 186
Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
1

Methods in constructors are dispatched as the dynamic type of the class. A's constructor is calling Foo with dynamic type A. (see AlfP.Steinbach's comment for the correct definition)

If A is an abstract base then the error is because it's trying to call a pure virtual method.

A()             
{
    this->Foo(); // call A::Foo
}

From Effective C++ by Scott Meyers:

There's a good reason for this seemingly counterintuitive behavior. Because base class constructors execute before derived class constructors, derived class data members have not been initialized when base class constructors run. If virtual functions called during base class construction went down to derived classes, the derived class functions would almost certainly refer to local data members, but those data members would not yet have been initialized. That would be a non-stop ticket to undefined behavior and late-night debugging sessions. Calling down to parts of an object that have not yet been initialized is inherently dangerous, so C++ gives you no way to do it.

Pubby
  • 51,882
  • 13
  • 139
  • 180
  • ahhh so you mean if i change my code to something like this public: A() { this->Foo(); } virtual void Foo() =0; }; it will throw error ??? – samantha Nov 28 '11 at 22:41
  • -1 "Constructors don't perform virtual calls - they call their own methods" is incorrect. – Cheers and hth. - Alf Nov 28 '11 at 22:42
  • @AlfP.Steinbach Could you elaborate? – Pubby Nov 28 '11 at 22:48
  • 2
    @Pubby: the effect of any call does not change depending on whether the call is issued in a constructor or not. The effect of a call of a virtual member function depends on the dynamic type of the object that the function is called on. In a class T constructor, the dynamic type of the object being constructed, is T. – Cheers and hth. - Alf Nov 28 '11 at 23:04
  • +1 I thought of the same quite and think it applies here. Also, if the type is always that of the object being created then the end result is what he stated it was. – John Humphreys Nov 28 '11 at 23:05
  • removed downvote since it's fixed – Cheers and hth. - Alf Nov 29 '11 at 01:18
1

Constructors will be called so that the parents are constructed first, so that there won't be any dependencies on undefined objects. Thus it's A::A followed by B::B. Edit: It's also possible that B's constructor calls A's directly, as Kerrek SB says - the end effect is the same.

In the first case, the output will be "A::Foo()" followed by "B::Foo()". At the time of A's construction B doesn't exist yet, and its virtual functions aren't yet part of the object.

In the second case, you'll be calling a pure virtual function A::Foo which will generate a fault or refuse to compile altogether.

Community
  • 1
  • 1
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622