Friday, June 11, 2010

OO design & C++ buzzwords

Here are some C++ important buzzwords that are worth knowing and worth looking into. The buzzwords are kind of mixed in the sense that some are basic and more regularly used and other are kind of advanced and not that much regularly used. So, I am not going to keep any kind of order in that sense. The second thing is – the description and explanation are not directly copied from the source. I have put down my own understanding about each term. The readers are open to leave comments if they find anything which is not properly defined or which does not seem to fit.
1. Virtual Destructor
Virtual destructor is a necessary thing when there is the possibility that a derived class object is deleted via a base class pointer. And when that is done, when the destructor is called, the base class does not know a damn about which class the object is originally and so, the corresponding destructor of that class is never called. This can introduce serious error because the derived class might have some resource to de-allocate. This can be shown by the following example.
The code is attached at VirtualDestructor.cpp
The output of the above code is
”Inside CBase Destructor”
Here, we can see that the derived class destructor is not called at all. To remove this problem, we need to declare the base class destructor as virtual. So, instead of writing the destructor as
~CBase1() throw()
{
cout<<"Inside CBase Destructor \n"; } we will write as follows virtual ~CBase1() throw() { cout<<"Inside CBase Destructor \n"; } This removes the problem. The output then becomes Inside CDerived Destructor Inside CBase Destructor The actual rule of when to declare the base class destructor as virtual is not trivial. But the rule of thumb (without hurting performance), we can say that, whenever a class has a virtual function (so, that class is likely to be derived in future), the destructor should be virtual.

2. Pure virtual destructor
Pure virtual destructor is the only class member function that has to be defined even after it is declared as pure virtual.
class CMyClass1{
public:
CMyClass1() throw();
virtual ~CMyClass1() = 0;
private:
};

CMyClass1::CMyClass1() throw(){
}

CMyClass1::~CMyClass1(){
}
So, why would someone need a pure virtual destructor? The argument is that, there is no reason why there cannot be a pure virtual destructor. As destructor is just a function like any other member function in a class, that is why there can be a pure virtual destructor just like any other functions. Apart from that there is no special reason why we need a pure virtual destructor. Some people say that we can make the destructor pure virtual so that we can turn a normal class into an ABC. But that is not a strong enough reasoning.
The special thing about pure virtual destructor is that we have to put a definition of the function even if it is pure virtual destructor. This is because the destructor must be defined for all classes as destructor are always called when objects are destroyed.

3. Inline virtual member function
A compiler can actually inline a member function which is declared as inline in the case when the compiler can statically resolve the actual type of the object which is called on that function. So, a virtual function can only be inclined when the object that is used to call it is a pointer or reference (because that case the type of the object must be resolved dynamically). So, only in the case when the compiler can resolve the type of the object statically, only then the compiler can keep an inline function as inline. Otherwise it won’t happen.
So, a very useful rule of thumb when declaring the functions as inline is to think whether it is going to be accessed by an object or a pointer or a reference to an object.
Here an important thing to remember, a compiler has the ,liberty to ignore the inline specifier even if the object can be resolved at compile time. So, we cannot say that the places where a virtual inline function is called via an object (the type of which the compiler can resolve statically) will always be inlined . But the reverse is always true (if an inline virtual function is called via a pointer or reference, that function calling part cannot be inlined because the compiler cannot resolve the type of the object at compile time).
N.B. A short note on what happens when an inline function is called. The compiler puts the inline member function code directly in that place removing the function call and thus removing some unnecessary stack operations made during a function call.

4. Virtual Constructor idiom
So, what to virtual constructors do? They can construct the actual object in the class hierarchy without knowing the actual type of the object during construction. The concept of virtual constructor comes into life when you are trying to construct an object of a class which you don’t know of (Which derived class). Like the following piece of code.
Class CBase{};
Class CDerived: public CBase{};

int main(){
CBase *f = new CDerived();
CBase* f2 = aObj.Clone(); // here f2 does not actually know that aObj is a CDerived
//Object }
So, how can we do it? We can do it using the virtual constructor idiom. The code is in VirtualConstructor.cpp.

5. Constructor initialization list
It is the best way to initialize the member variables of an object. It is always better to use the constructor initialization list rather than manually creating values to the member variables. It works in the following way:
class CPerson{
public:
CPerson(string& aName) throw(bad_alloc);
private:
string iName;
};

CPerson::CPerson(string& aName) throw(bad_alloc)
:iName(aName)
{}
In the above example, instead of manually initializing the string variable in the constructor body, we just use the initilization list.

6. Virtual friends
The following code can be used as a virtual friend
VirtualFriend.cpp
Using the above style, we can get a virtual friend function – which depending on the runtime type of the object, calls the appropriate function in the class hierarchy.

7. Initialization order dependency
The initialization order dependency means , no matter how the member objects are ordered in the initialiers list, the initialization will occur in the same order in which the members are declared In the class declaration. An example will help to understand the issue. InitializationOrder.cpp
From the code, if we run it, we will see that the member objects are written in the opposite order on the CMyClass constructor initilizers list that the way in which they are declared in CMyClass. But during construction, the copy constructor of the two classes ( during the execution of the initialiers list in CMyClass constructor) ,are called in the same order in which they are declared in the class, not by the order in which they are declared in the initializes list.

8. Argument screening
It is nothing actually. Argument screening means the sanity checking of the parameters of a constructor. In that case when argument screening is needed, the code for screening becomes too much and then we cannot initialize the argument using constructor initialization list.

9. The {…} initializer syntax
This is actually nothing compared to how intimidating it sounds. It is actually the array initialization syntax. Take a look at the code InitializerSyntax.cpp
In the above example we can see that the first element of the array is handed an already constructed object. So, it explicitly calls the copy constructor.

10. The overloaded parenthesis operator [operator ()]
The overloaded parenthesis operator can take as many parameters as one would like to pass into the operator function.
Just look at the following code OverloadedParenthesis.cpp
In the above code, the () operator has been overloaded thrice to do three kind of things. The one returning the reference to double is used to set the value;
The one returning the const double is used to get the value of the array element.
And the last one with no parameters is used to reset the array inside the object.
But we have to remember one thing that I have said in an earlier post. Do not do anything for the sake of doing it. The whole idea of operator overloading is to ease the user of the class and make him comfortable by providing all the common an intuitive usage of the operator . In the above example the three overloaded operators and their functionality is not intuitive. The only scenario in which this overloaded parenthesis operator can be intuitive and appropriately useful is the matrix class.That is because we cannot define overloaded double subscript operator Matrix[][] ,, but we can use something like Matrix(x,y); Which looks pretty much same as the subscripted version.

Source1
Source2

0 comments: