tags:

views:

112

answers:

6
#include <iostream>
#include <string>
class A
{
public:
    A(int a) : _a(a) {}
    virtual ~A() {}
    virtual void f() const {std::cout << _a << std::endl;}
private:
    int _a;
};
class B : public A
{
public:
    B(int a, int b) : A(a), _b(b) {}
    virtual void f() const {std::cout << _b << std::endl;}
private:
    int _b;
};
int main()
{
    B b (1,2);
    A a (5);
    A& ref = a;
    ref = b;
    ref.f();
    return 0;
}

Output:

1

I understand that when copying derived (extended) class object to the base class object, the derived object is being cut and only the base class data copied. But i thought that the virtual table of 'ref' now should be as a virtual table of 'b' so 'ref.f();' should call the function:

void B::f() const {std::cout << _b << std::endl;}

But after the copying the vtbl of 'ref' remains to be the vtbl of class A. why? Thanks.

+1  A: 

If you use pointers (instead of refences), your derived objects stay as they should. Then the vtbl is called correctly.

A* ref;
ref = &b;
ref->f();

Wen you use references, the runtime system doesn't understand that what you had in ref is a B. It thinks its an A when you do the assignment.

Starkey
@Starkey: I tought, that the pointer to the vtbl acts like other members of the class, and being copied like any other member. Isn't it true?
Sanich
@Sanich: No, the vtable is not a member. It reflects and defines the dynamic type of the object.
Potatoswatter
+2  A: 

A& ref = a; ref = b;

The static as well as dynamic type of your reference is A& . Assignment ref = b doesn't change the dynamic/static type of the reference as per your expectation.

From Marshall Cline's C++ FAQ

You can't separate the reference from the referent.

Unlike a pointer, once a reference is bound to an object, it can not be "reseated" to another object. In that sense, a reference is similar to a const pointer such as int* const p.

Try this

A& ref = b;  //reference is bound to b [dynamic type of the referent is `B&`]) 
ref.f(); //Prints 2
Prasoon Saurav
@Prasoon Saurav: As i understand, every virtual class has a pointer to its virtual table. My question actualy is: What happens to that pointer when the object being copied? Is it copied as a regular member or not? Thanks!
Sanich
@Sanich : Check out the edits.
Prasoon Saurav
+1  A: 

No, the virtual table does not act as a pointer, and is not being copied when you assign to a base-class instance (this is called slicing).

First, let's clarify your code:

A& ref = a;
ref = b;

Is directly equivalent to:

a = b;

Now, what happens here, is that the A instance's contents are being replaced by the the A part of the B instance's contents. However, the result is still an A instance, therefore, it points to A's virtual table, correctly invoking A::f.

André Caron
It's even more directly equivalent to `a.operator=(b)` or `A::operator=(a,b)`, which (IMHO) better emphasises the fact that the assignment is modifying the object `a` rather than updating the reference `ref`.
Zooba
A: 

I think you got confused by Reference vs. Pointer. Make yourself aware of what your code actually does.

A& ref = a;   // Reference
ref = b;

After the first line, ref and a are two names for the same object. The effective type of ref is A.

The second line assigns the object b to the object referenced by ref (which is, of course, a). You now have

  • the original b, and
  • the A-part of b copied into the object a (and referenced by ref).

After the copy, any "B-ness" of the copied object is utterly lost, along with the previous contents of the object a. The _a member got copied, the _b member and the B vtable got spliced off.

Try this instead:

int main()
{
    B b (1,2);
    A a (5);
    A * ref = &a;  // pointer, not reference
    ref = &b;
    ref->f();
    return 0;
}

This yields "2", as you probably expected. It also doesn't overwrite the object a, as the first example did.

DevSolar
+2  A: 

Virtual table (or, more precisely, pointer to the virtual table) stored in each polymorphic object is never copied.

The reasons behind your "why" question is not exactly clear. Of course, it is not copied. Why should it be copied? Virtual table is a way to describe the type-specific behavior of a concrete object. It basically [indirectly] describes the actual type of the object. Since the actual type of the object does not change during copying, the virtual table remains the same.

AndreyT
+1  A: 

Firstly 'virtual table' is not a standard C++ concept. It is a highly implementation specific mechanism to implement dynamic binding and implement virtual functions.

Having said that,

But i thought that the virtual table of 'ref' now should be as a virtual table of 'b' so 'ref.f();' should call the function

This is not correct. Virtual table is per class and not per object. It is only Vptr that is per object.

The type of 'ref' (as confirmed by typeid(ref).name if you so please) is 'A &'. When you assign 'ref = b' the implicit assigment operator of 'A' is called with object 'b' as the argument. This operator just blindly copies the 'A' subobject of 'b' into the current object referenced by 'ref' (which is 'a'). Hence object 'a' now has the exact same state as the 'A' subobject of 'b'.

As you can see, in this whole very long story, VTABLE and VPTR does not exist at all!.

Chubsdad