views:

207

answers:

3

I'm reading Thinking in C++ by Bruce Eckel. In Chapter 15 (Volume 1) under the heading "Behaviour of virtual functions inside constructor", he goes

What happens if you’re inside a constructor and you call a virtual function? Inside an ordinary member function you can imagine what will happen – the virtual call is resolved at runtime because the object cannot know whether it belongs to the class the member function is in, or some class derived from it. For consistency, you might think this is what should happen inside constructors.

Here Bruce's trying to explain that when you call a virtual function inside an object's constructor, polymorphism isn't exhibited i.e. the current class' function will only be called and it will not be some other derived class version of that function. This is valid and I can understand it, since the constructor for a class will not know beforehand if it's running for it or for someother dervied object's creation. Moreover, if it does so, it'll be calling functions on a partially created object, which is disastrous.

While my confusion suddenly arose because of the first sentence where he states about the ordinary member function, where he says the virtual call will be resolved @ run-time. But wait, inside any member function of a class, when you call another function (be it virtual or non-virtual) it's own class version will only be called, right? E.g.

class A
{
    virtual void add() { subadd(); }
    virtual subadd() { std::cout << "A::subadd()\n"; }
};

class B : public A
{
    void add() { subadd(); }
    void subadd() { std::cout << "B::subadd()\n"; }
};

In the above code, in A::add() when a call to subadd() is made, it'll always call A::subadd() and the same holds true for B as well, right? So what is he meaning by "the virtual call is resolved at runtime because the object cannot know whether it belongs to the class the member function is in, or some class derived from it" ?

Is he explaining it with respect to a call via a base class pointer? (I really suspect so) In which case he shouldn't be writing "Inside an ordinary member function"; from my understanding so far, any call of a member function from inside another member function of the same class is not polymorphic, please correct me if am getting it wrong.

+3  A: 

when you call another function (be it virtual or non-virtual) it's own class version will only be called, right?

Sorry, wrong. The constructor is the exception. Otherwise, you are dealing with a fully constructed object for which full polymorphism is in place. In your later example (if B inherits from A), what is called is the B::subadd() (just take the code for a test drive: that's really the best way to learn how a programming language works).

Pontus Gagge
Ah: I didn't read the code, but what you intended. Surely you intended B to inherit from A?
Pontus Gagge
Yes yes, sorry I didn't write it properly in the post, fixed now. Thanks :)
legends2k
+1  A: 

The call subadd(); in your examples is really the call this->subadd();. In A::add(), the type of this is A*, but this might point to a C object (assuming C derives from A too). When compiling A::add(), the compiler can't know which virtual functions are overridden in class C. The call this->subadd(); in A::add() may actually call C::subadd(). Thus, the compiler has no choice but putting in a virtual call, to be resolved at runtime when it knows where this-> points to.

MSalters
I agree, but when will an `A*` point to C object; only if outside the class, some assignment is made and called (to achieve polymorphism). I'm asking about calling a member function normally i.e. `obj.member();`, where member() calls `submember()` which is virtual in the hierarchy.
legends2k
Thanks for the explanation about `this` here, since I was a bit confused that `this` is always the `Class*`, but it can be `Caller*`, like you said `this` is `A*` for a `C` object if called from a `A` pointer.
legends2k
+2  A: 

You are wrong - a further derived class could override some of the virtual functions, meaning that a static call would be wrong. So, to extend your example:

class C : public B
{
public:
    // Not overriding B::add.
    void subadd() { std::cout << "C::subadd\n"; }
};

A *a = new C;
a->add();

This dynamically calls B::add, which in turn dynamically calls C::subadd. A static call to B::subadd would be wrong, since the dynamic type is C and C overrides the function.

In your example, the duplication of A::add as B::add is unnecessary - both will call subadd polymorphically whatever the dynamic type of the object.

Mike Seymour
Thanks for showing this example, it made me understand clearly. `A::add()` called `C::subadd()` when I tried it. I now get it, any call to a virtual function (be it residing anywhere) cannot be static, but should be dynamic. +1 and thanks!
legends2k
"I now get it, any call to a virtual function (be it residing anywhere) cannot be static"That's not correct.If the compiler knows the exact type, it doesn't need to make a virtual call.
Eric H.
I agree, it'll do a static call when it knows for sure that the object is of type A and there's no need for polymorphism. E.g. `A a; a.subadd();`
legends2k
@Eric: Although it's true outside the class functions, but inside member functions, it doesn't know if `this` is `Class*` are `Caller*` and hence does late binding, always.
legends2k