Dissecting Code

Monday, July 12, 2010

Of constants and R and L values

 

RValue: A value that can appear on the right of an expression only
LValue: A value that can appear on the left of an expression. An LValue is converted to an RValue implicitly. The vice-versa does not hold.

An important point: only LValues can be bound to references to non-constants. A temporary object is a RValue.

Consider the following:

#include <iostream>

using namespace std;

class A
{
public:
~A(){ cout<<"A"; }
};

class B: public A
{
public:
~B(){ cout<<"B"; }
};

B factory()
{
B temp;
return temp;
}


int main()
{
const A& obj = factory();
return 0;
}



This is valid in standard C++, as the const used along with the reference forces the life of the temporary object to the end of the scope of the block rather than the scope of the expression (which is generally the case for temporaries). The const in necessary for performing the above magic. Note here, we have no need for a virtual destructor too!!



Source: http://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/

Labels: ,

Wednesday, February 17, 2010

Explicit, implicit constructors, the = operator and the member initialization list

Consider this piece of code:

#include <iostream>

class B
{
int m_a;
public:
B(){}
B(int a){m_a=a;}
B& operator=(const B &b1){m_a=b1.m_a; return *this;}
int Print()
{
std::cout<<"Hi there";
}
};

class A
{
B m_var;
public:
explicit A(int a)
{
m_var=a;
}
};

int main()
{
A a(3);
return 0;
}



This gives quite an insight into the working of constructors, and the usage of the assignment operator. I’ll step through the code:


  • As expected, the constructor for A is invoked.

  • As the control enters A’s constructor, constructor for B is called, that initializes m_var with a junk value.

  • Constructor for B is called to create an object from int a (as B’s constructor is not explicit, this is allowed by the compiler).

  • B’s = operator is invoked to assign the value of the created temp object to m_var.


Clearly seen is that it would have been better to use the member initialization list to assign B with something like:


#include <iostream>

class B
{
int m_a;
public:
B(){}
B(int a){m_a=a;}
B& operator=(const B &b1){m_a=b1.m_a; return *this;}
int Print()
{
std::cout<<"Hi there";
}
};

class A
{
B m_var;
public:
explicit A(int a): m_var(a)
{
}
};

int main()
{
A a(3);
return 0;
}


In this case:

  • A’s constructor is invoked.

  • B’s constructor is invoked that sets m_var.m_a to a.


Thus member initialization lists are there for a reason. Use them!

Labels: , , , ,