views:

110

answers:

2

I'm so confused with the output of the following code:

#include <iostream>

using namespace std;

class Parent
{
public:
    Parent() : x(2) { }
    virtual ~Parent() { }

    void NonVirtual() { cout << "Parent::NonVirtual() x = " << x << endl; }

private:
    int x;
};

class Child : public Parent
{
public:
    Child() : x(1) { }
    virtual ~Child() { }

    void NonVirtual() { cout << "Child::NonVirtual() x = " << x << endl; }

private:
    int x;
};

int main()
{
    Child c;
    Parent* p = &c;

    c.NonVirtual();  // Output: Child::NonVirtual() x = 1
    p->NonVirtual(); // Output: Parent::NonVirtual() x = 2
    // Question: are there two x'es in the Child object c?
    //           where is x = 2 from (we have not defined any Parent object)?

    cout << sizeof(c) << endl;
    cout << sizeof(*p) << endl;

    return 0;
}
+5  A: 

The code above illustrates the fact that, in C++, only functions marked virtual are overriden. What you have here is overshadowing, not overriding. In overriding and inheritance, the behavior is based on runtime type which is the normal inheritance behavior you expect, but if you don't declare it virtual, then the behavior is based purely on compile-time type (i.e. declared type). Since p is declared to be of type Parent* it uses the implementation in Parent, while c is declared to be of type Child, and so it uses the version given by type Child. If you declared the method virtual, then in both cases, it would lookup the appropriate version of the function at runtime and invoke the version given in the Child class.

I should also add that you have two different x variables... if you want to share variables between a base class and a derived class, you should mark it "protected" in the base class (although I would argue that it is generally poor design to do so). The x variable that you declare in Child is a different variable from the one in Parent. Remember that x is private in Parent, and so the name x didn't have any meaning in Child until you created a second variable named x in Child.

Michael Aaron Safyan
@Michael: I agree with what you said. but my questions are (also in the code comments): are there two x'es in the Child object c? where is x = 2 from (since we have not defined any Parent object)?
Peter Lee
@Peter Lee, yes, and "x" will refer to the one that is visible in that class's scope.
Michael Aaron Safyan
@Michael: I also agree with what you said in the newly added 2nd paragraph, but still the question: where is x = 2 from? Is it from a Parent object? If yes, where is the Parent object (since we have not defined any Parent object)?
Peter Lee
@Peter, the Child object inherits from the Parent object... thus it has all the state and functions of the Parent object (the ": public Parent" declares that Child inherits from Parent).
Michael Aaron Safyan
When Child is constructed, the Parent class default constructor is automatically called before the Child constructor. This assigns Parent::x to 2. Then Child's constructor is called setting Child::x to 1. Since each class has its own x, the one you get depends on the run-time type.
Jason
@Michael: as you said, "thus it has all the states and functions of the Parent object", if this is true, sizeof(c) is not reliable, which is confusing me again.
Peter Lee
@Jason: I agree with you, but the question is where x(2) is stored?
Peter Lee
@Peter, the derived class has all the state and functions of the base class *in addition to* its own state, so it does make sense. We would expect that sizeof(Child) == sizeof(Parent) + sizeof(int), assuming that there is no sort of padding going on. Test sizeof(int) -- I'm guessing it is 4 on your platform --, so it makes sense.
Michael Aaron Safyan
@Peter, how the class is layed-out it dependent on the compiler ABI, but most likely it lives in the address right before Child::x.
Michael Aaron Safyan
Any Child object contains both. It contains the one it inherits from Parent and the one it declares itself. Note that this is probably not good practice since it leads to this kind of confusion. If you want Child to only contain one version of x, don't redeclare it and just let it use the inherited x from parent. If you want two different variables, name them something different. Also, if Parent::x was public or protected, you could access it in the member functions of Child by specifying that you want the Parent version of x with `Parent::x`.
Jason
Peter Lee
There's no need to be ashamed about getting the number of vtables wrong. It's an implementation technique, not something mandated by the standard. And it's possible for an object to have more than one vtable; that's how multiple inheritance is implemented.
Max Lybbert
@Max, what I'm ashamed of myself is that I didn't know there are two x'es in Child, which I should have known. By the way, you are right that the multiple inheritance uses several virtual pointers.
Peter Lee
A: 

There is nothing to be confused here. When you invoke a non virtual function, whatever type that has been assigned to, the methods of that type gets invoked.

Here, Parent* p, and the child's address are the same, but the assignment is to the Parent. Thus, all methods that are of non virtual invokes the parent's methods and not the child's one.

Remember, when you use inheritance (especially public one), the child automatically derives all parent's methods and members. Thus, even though, you assign the child's pointer, but assignment takes place to the parent's type.

The only thing you need to make sure is you don't do delete P here as both P and child C, shares the same address.