References curio

ajb came across an interesting C++ quirk at work today that I think is worth making a note of:

include <iostream>

class Base
{
public:
  virtual void Hello()
  {
    std::cout << "Base::Hello - Hello World!" << std::endl;
  }
};

class Derived : public Base
{
public:
  virtual void Hello()
  {
    std::cout << "Derived::Hello - Hello World!" << std::endl;
  }
};

int main(void)
{
  Base b;
  Derived d;

  Base &bRef = b;
  Derived &dRef = d;

  bRef.Hello();
  dRef.Hello();

  bRef = dRef; // What’s going on here, then?

  bRef.Hello();

  return 0;
}

This works as you’d expect right up until that bRef = dRef line. This is where things get a little weird. We’ve assigned bRef (which is a reference to b) to dRef (which is a reference to d), so when we call bRef.Hello() we should get dRef’s (ie, d’s) Hello(), right?

Well, wrong. We get b’s Hello(). Which is kind of confusing if you’re used to thinking of references as a sort of “pointers without the *s” like many people do.

A reference to a variable is simple another name for that variable. If we declare bRef to be a reference to b, for all intents and purposes, bRef is b. Operations on bRef take place in exactly the same way as they do on b.

So therefore, what’s happening on our bRef = dRef line? Well, the compiler sees bRef and goes “Ah, that’s another name for b”; then it sees dRef and goes “Ah, that’s another name for d”. So what we’re actually doing is b = d – all the members of d get copied into the corresponding members of b, but – and this is the critical bit – b, and hence bRef, is still the same object as it was before; it does not magically become a Derived object. All we’re doing is a copy assignment.

The effect can be better seen if you add a member variable to Base, and initialise it to one thing in Base’s constructor, and something else in Derived’s constructor. You’ll see that, after the bRef = dRef assignment, that member will equal whatever it was set to in Derived’s constructor – copy assignment has taken place, but bRef is still a reference to b.

One Response to “References curio”

  1. Clive says:

    I’m not quite sure why that should confuse people who think of references as pointers without the *s.

    After all, it’s equivalent to:
    Base *bPtr = &b;
    Derived *dPtr = &d;
    *bPtr = *dPtr;
    …which is exactly what you ought to expect, surely?