Meditation, The Art of Exploitation

Thinking? At last I have discovered it--thought; this alone is inseparable from me. I am, I exist--that is certain. But for how long? For as long as I am thinking. For it could be, that were I totally to cease from thinking, I should totally cease to exist....I am, then, in the strict sense only a thing that thinks.

Friday, April 20, 2007

reference type cannot be reseated in C++

In C++, a variable can be declared as a reference to a referent object. There are some benefits with reference types, they must be initialized upon construction (note not declaration), for example,

int x;
int & rx = x;
// int & rxa = x ! Compile error: error: ‘rxa’ declared as reference but not initialized

class A {
int & rx;
A (int x): rx(x){ }
};


The first case is initialization upon construction via declaration. The 2nd case however shows that initialization only happens at construction. One can declare a reference type class member without initializing it until class object construction time.

There is a subtle implication here related to STL container types. The STL containers usually require that an object can be default constructed, copy constructed and copy assigned. When one declare a class with reference type class members, it becomes a problem how to properly initialize the reference type class members at construction time. Remember that a default constructor takes no argument, therefore one cannot initialize the class member through a constructor argument such as shown in the example above (by definition it's no longer a default constructor). One must declare a default constructor in addition to other ones that constructs with arguments. One technique is to use a global variable as a referent for reference type class members such as:
int global;
class A{
int x;
public:
A (): x(global){}
};

But this kind of coding practice is beyond style issue, what's the point of having class members that refer to a global variable that's visible to the entire code scope anyway? Since they will all refer to the same referent global object, it's also less efficient to declare a reference per object. Sadly there does not seem to be any way out of this C++ standard. Here the fix is not to use a reference type but a pointer because pointers are not required to be initialized upon construction. As one can see, the key difference between reference and pointer is 'reference is initialization upon construction', this makes reference much safer to dereference than pointers but at the same time limits its usefulness.

There is one important property of reference type. That is, reference type cannot be reseated! Once a reference type is initialized upon construction, it's bound to the referent. One cannot change the referent it's bound to through out the reference type lifetime. Often people get confused that one can assign new variables or references to a reference after its initialization, consider the following examples,

int x;
int & rx = x;
x = 3;
int y = 4;
rx = y;
int & ry = y;
rx = ry;

One may think that rx is changed to refer to different object looking at the code. But it's wrong. rx is referring to x exclusively. The value of x is changed every time rx is used as an assisgnee. Remember that reference type cannot be reseated, it's simply an alias of its referent upon its initialization. The above example is equivalent with:
int x;
int & rx = x;
x = 3;
int y = 4;
x = y;
int & ry = y;
x = ry;

Simply replace every occurrence of rx with x, and you see what's going on. As an exercise, see if you can figure out the output of the following code correctly, if you did, then reference type reseating issue is no longer a myth to you:
#include
using namespace std;

int main(){

int x = 1;
int & rx = x;
rx = 2;
cout << "x = " << y =" 3;" class="blsp-spelling-error" id="SPELLING_ERROR_28">rx = y;
cout << "x = " << class="blsp-spelling-error" id="SPELLING_ERROR_30">rx = 4;
cout << "y = " << class="blsp-spelling-error" id="SPELLING_ERROR_32">ry = y;
rx = ry;
ry = 4;
cout << "x = " << class="blsp-spelling-error" id="SPELLING_ERROR_37">cout << "y = " << y << '\n';

}