Tuesday 16 January 2007

delete this;

"delete this;" legal?

Yes! of course its legal, with certain restrictions.

Once you call 'delete this;' no member variables should be accessed, since they would have been deleted already.


class CTest
{
    int iInt;
    char *iStr;

    public:
        void func()
        {
            ...

            delete this; // object destroyed here

            ...

            iInt = 6;// Illegal! don't do this
        }
};


Also this is not valid if the object is created on stack. Following should not be done.


CTest obj;
obj.func(); // Not valid since obj is on stack

 

CTest *ptr = new CTest();
ptr->func();


// Valid, only if no members are accessed after delete this;


 


One more interesting fact! What if we use 'delete this;' in destructor? ;)
Of course it will create the recursion calls to destructor!!!


 

Is there any practical use of this then?

Yes!

Consider the scenario where I would want to restrict the user to be able to create an object only on heap. User should not be able to create the object on stack.

We can achieve this by making destructor private. There by preventing the stack unwinding to happen, which will intern avoid creating variables on stack. Then the user will only be able to create object on heap.


class COnlyOnHeap
{
    private:
        ~COnlyOnHeap(); // destructor made private
};

COnlyOnHeap obj; // Not possible since d'tor is private.
COnlyOnHeap *ptr= new COnlyOnHeap(); // Possible


We achieved our goal!


Object can only be created on heap but not on stack.


But, Wait!

How will u cleanup this?


We cannot say "delete ptr", because d'tor is private.


How will we cleanup this then?


The only way is to expose one more cleanup public function which does "delete this;"!



COnlyOnHeap::Cleanup()
{
    delete this; // This calls the private d'tor within the class
}


Enjoy C++

 

Implementing a "final" class in C++

The key word "final" exists in java world. It means, no class can inherit from the class declared as "final". A class which cannot be inherited further.

The simple method by which we can achieve this is to make the constructor of the class private.


class CFinal
{
    private:
        CFinal(); // Private constructor to avoid inheritance

    public:
        static CFinal *GetInstance();


       // static public member function to create the instance
};


This method looks very simple. If we try to derive another class from CFinal and try to instantiate that class, compiler errors out saying cant access CFinal's constructor, since its private.

But, this method has its own drawbacks,


  • Need to have a static public member function to create instance.

  • If the class needs to have more than one type of c'tors, all these c'tors need to be replaced by corresponding version of static-public-member function.

  • Object cannot be instantiated on stack.

What if we make destructor private, instead of c'tors?
We can achieve the "final" thing.
But, this too cannot be instantiated on stack (stack-unwinding needs d'tors to be public).


We can club private d'tors, private c'tors, friend-class and virtual inheritance concepts to achieve a good solution.


class CFinal;  // Forward declaration

class CFinalLocker
{
    private:
        CFinalLocker();
        ~CFinalLocker();

    friend class CFinal;
};

class CFinal : public virtual CFinalLocker
{
    ...
};

class CDerived : public CFinal
{
    ...
}

CFinal objFinal; //OK - no problem
CDerived objDer; // Error! - Cant access CFinalLocker's c'tor


Following is the useful explanation of the above technique


  • CFinalLocker which is the base class for CFinal will have all of its c’tors and the destructor private.

  • CFinal has been made friend of CFinalLocker. This is to facilitate CFinal to access all the members of CFinalLocker including members which are private. This will allow us to instantiate CFinal.

  • We inherit our CFinal virtually from CFinalLocker. Now whenever anyone attempts to inherit class from CFinal and try to instantiate it, then its constructor tries to call the constructor of CFinalLocker. But the constructor of Temp is private so compiler complains about this and it gives error during the compilation, because your derived class is not friend of CFinalLocker. Remember friendship is not inherited in the derived class.

  • The purpose of inheriting CFinal virtually from CFinalLocker : the object of most derived class (whose object is created) directly calls the constructor of virtual base class (to avoid diamond problem). Now the most derived class (Bottom in case of diamond problem) directly calls the most base class (Top in diamond problem) constructor. So when we try instantiating CDerived compiler errors out since the c’tor in CFinalLocker is private.

Friday 12 January 2007

Thursday 11 January 2007

Why can't we have "Virtual Constructors"?

Virtual mechanism acts on 'complete objects'.
An object is complete 'after' the constructor has finished executing.
So, it is illegal to make constructors virtual.
Destructors can be virtual because by the time you apply them object has been constructed completely.

Need for "Virtual Destructor"

If we have 2 classes, say

class CBase{
public:
CBase(){}
~CBase(){}
};

class CDerived : public CBase{
public:
CDerived(){}
~CDerived(){}
};

and if we do,

CBase* pBase = new CDerived;
delete pBase;

Since CBase does not have a virtual destructor, the delete pBase invokes the destructor of the class of the pointer (CBase::~CBase()), rather than the destructor of the most derived type (CDerived::~CDerived()). And as you can see, this is the wrong thing to do in the above scenario.

A class must have a virtual destructor if it meets both of the following criteria:

  • You do a delete pBase.
  • pBase actually points to a derived class object.

Assignment operator Vs. Copy constructor

The copy constructor is for creating a new object. It copies a existing object to a newly constructed object.

The assignment operator is to deal with an already existing object.


class MyObject
{
    public:

        MyObject(); // Default constructor
        MyObject(MyObject const & a); // Copy constructor

        // Assignment operator
        MyObject & operator = (MyObject const & a)
};


The copy constructors play an important role, as they are called when class objects are passed by value, returned by value, or thrown as an exception.


// A function declaration with an argument of type MyObject,
// passed by value, and returning a MyObject

MyObject f(MyObject x)
{
    MyObject r;
    ...
    return(r); // Copy constructor is called here
}

// Calling the function :

MyObject a;

f(a); // Copy constructor called for "a"


It should be noted that the C++ syntax is ambiguous for the assignment operator.


MyObject x; x=y;


and


MyObject x=y;


have different meaning.


MyObject a; // default constructor call

MyObject b(a); // copy constructor call

MyObject bb = a; // identical to bb(a) : copy constructor call

MyObject c; // default constructor call

c = a; // assignment operator call

 

Overloading Vs. Overriding

  • Overloading is nothing but static binding.

  • Overriding is dynamic binding which will be resolved at run-time.

  •  



  • Overloading deals with multiple methods in the same class with the same name but different signatures.

  • Overriding deals with two methods, one in a parent class and one in a child class, that have the same signature.

  •  



  • Overloading lets you define a similar operation in different ways for different data.

  • Overriding lets you define a similar operation in different ways for different object types.

  •