3

As I understand it, polymorphism with references should work exactly as it does with pointers.

However, consider the following example: the calls to doer() are correctly dispatched when using pointers, but the "B version" seems to be called in both case when using a reference.

I cannot figure out the reason why the following example is behaving in the way it does. Why is the "B version" called in both cases when using a reference?

#include <iostream>

class B;
class C;

void doer(B *x) {
    std::cout << "B version" << std::endl;
}

void doer(C *x) {
    std::cout << "C version" << std::endl;
}

class A {
public:
    virtual ~A() {}
    virtual void doit() = 0;
};

class B: public A {
public:
    virtual void doit() override {
        doer(this);
    }
};

class C: public A {
public:
    virtual void doit() override {
        doer(this);
    }
};

int main() {
    B b;
    C c;

    A *a = &b;
    a->doit(); // B version gets called, OK
    a = &c;
    a->doit(); // C version is called, OK

    A &d = b;
    d.doit(); // B version is called, OK
    d = c;
    d.doit(); // B version is called again??
}
Christophe
  • 68,716
  • 7
  • 72
  • 138
Stefano Sanfilippo
  • 32,265
  • 7
  • 79
  • 80

2 Answers2

3

Here you assign a reference:

A &d = b;
d.doit(); // B version is called, OK

Here you overwrite the object reffered to by d with c (it's no longer the definition of a reference):

d = c;
d.doit(); // B version is called again??

That's the main difference between references and pointers. A reference is like a constant pointer that you can only assign at definition. Afterwards, whenever you use the reference, it means the object you've referred to.

In fact when you do d = c; some slicing occurs. The object d is in fact a B, but the operator= from A is called, copying only member data of A.

Here how to demonstrate this statement:

class A {
public:
    ...
    A& operator= (A a) {
        cout << "A::operator=" << endl;  // just to show what happens when d=c is called
        return *this; 
    }
};
class B : public A {
public:
    int x;   // add some variables for B
    virtual void doit() override {
        cout << "B::doit() " << x << endl; 
        doer(this);
    }
};
class C : public A {
public:
    int a,b; // add aditional variables for C
    virtual void doit() override {
        cout << "C::doit() " << a << ","<<b << endl;
        doer(this);
     }
};
...
b.x = 123;  // put some class specific variables in C
c.a = 222;  c.b = 333;  // put some class specific variables in C
A &d = b;  // assignement of the reference.  d reffers to a b object
d.doit();  // B version is called, OK
d = c;     // but an A object is copied (so only A subobject of c is taken
           // to overwrite A subobject of d)
d.doit();  // B version is called becaus again?? => yes !! because it's still a B
           // And you see that the B part of the object is left intact by A::operator=
cout << typeid(d).name() << endl; 
           // remember at this point that d still refers to b !
Christophe
  • 68,716
  • 7
  • 72
  • 138
  • 2
    A reference is not a pointer. It can implemented as such, but that's implementation defined. – edmz Mar 01 '15 at 19:50
  • @black I completely agree. I meant that it's **like** a pointer (because this is something that OP seems to master well). I've edited to avoid any ambiguity here ! – Christophe Mar 01 '15 at 20:12
  • I'd suggest adding this [clarification](https://stackoverflow.com/questions/57483/what-are-the-differences-between-a-pointer-variable-and-a-reference-variable-in) to remove any further doubt, IMHO. – edmz Mar 01 '15 at 21:32
  • 1
    While the answer is here, it's diluted in a few unnecessary details imho. @0x499602D2 got the key detail, that assigning to a reference simply calls the assignment operator on the lhs. I know what a reference is, I know what an assignment operator is and how it behaves, I simply didn't realize what was happening. – Stefano Sanfilippo Mar 02 '15 at 10:49
1

References are bound by their referent for their entire lifetime, and, unlike pointers, require initialization. Using the assignment operator calls the assignment operator of the referent, and does not reassign the reference:

A &d = b;
d = c;

Here d = c calls the assignment operator from the base class A subobject contained in d, and copies the A subobject data contained in c.

David G
  • 94,763
  • 41
  • 167
  • 253