C++ reference not to be confused with Java/Python/... reference

· Read in about 3 min · (429 Words)

This program assigns a new object of class A to reference a. It looks as expected, reference changes to new object ( see line 23,24,25):

#include <iostream>

using namespace std;

class A {
  string name;
public:
  A(string n): name(n) {
  }
  A(): name("NO ONE") {
  }

  void print() {
    cout << "I am "<< name << "!"<< endl;
  }
};

int main(int argc, char *argv[]) {
  A matrix = A("the matrix");
  A &amp;a = matrix;
  a.print();

  // What happens here?
  a = A("neo");
  a.print();
}

OUTPUT:

I am the matrix!
I am neo!       

However the problem wasn’t apparent until I coded the following inheritance hierarchy.

Consider the following file vitual.cc which emulates a concept of «Intefrace» from Java world. All went well until I found an un-expected behaviour at lines 53 and 54 highlighted below:

#include <iostream>

using namespace std;

class AbstractInterface {
public:
  // constructor
  AbstractInterface() {
    cout << "AbstractInterface()" << endl;
  }
  
  // destructor
  virtual ~AbstractInterface() {
    cout << "~AbstractInterface()" << endl;
  }

  virtual void fun() = 0;
};

class A: public virtual AbstractInterface {
public:
  void fun() {
    cout << "A::fun()" << endl;
  }
};

class B: public virtual AbstractInterface {
public:
  void fun() {
    cout << "B::fun()" << endl;
  }
};

class AB: public A, public B 
{
public:
  void fun() {
    cout << "AB::fun()" << endl;
  }
};

int main(int argc, char *argv[]) {
  AB ab;
  A &amp;a = ab;
  B &amp;b = ab;
  AbstractInterface &amp;i = ab;
  ab.fun();
  a.fun();
  b.fun();
  i.fun();

  //What happens here?
  a = A();
  a.fun();
}

OUTPUT:

AbstractInterface() 
AB::fun()           
AB::fun()           
AB::fun()           
AB::fun()           
AbstractInterface() 
~AbstractInterface()
AB::fun()           
~AbstractInterface()

The output specific to lines 53 and 54 is:

AbstractInterface() 
~AbstractInterface()
AB::fun()           

Notice that an Object was created and destroyed immediately before we called a.fun(). If reference was changed to a new object, it would print A::fun() instead of AB::fun().

What happened here is:

  • A new object is created on the stack
  • reference assignment calls a copy constructor
  • then the object destructor gets called
  • the reference still points to old object

According to Thinking in C++ by Bruce Eckel:

There are certain rules when using references:

  • A reference must be initialized when it is created. (Pointers can be initialized at any time.)
  • Once a reference is initialized to an object, it cannot be changed to refer to another object. (Pointers can be pointed to another object at any time.)
  • You cannot have NULL references. You must always be able to assume that a reference is connected to a legitimate piece of storage.

So the concept of reference in C++ and Java/Python/Ruby… is not same!