Wednesday, February 14, 2007

A Puzzle in C++

Today i want to introduce you a puzzle :) Indeed, it is a design problem! We will define the problem and explain the solution step by step. If you are ready lets begin!

One day someone came to me and said that "Hey emreknlk, i need a special class that at one time only one instance has to be allowed to live in the memory. I use this class as a special source consumer so there shouldnt be more than one instance of this class. At most one instance, do you understand me ha?"

and i said, "Hey john, be relax man, it is easy to implement. just watch it".

So, you understand the problem. We design a class that at most one object is allowed to be created. I mean, if there is no object around, you could create one. However, if there is an object, so there shouldnt be one more!

Before reading the solution, you can try to solve it by yourself.

Ok, let's implement this class otherwise john will be angry :p

There may be multiple solutions to this design problem. If you design in another way you can share it with us by putting a comment;)

Firstly i want to put the constructor of the class to the private area! Noone can create an object directly! Lets say the class name will be "CUnique".

CUnique inst; <- by putting the constructor to private, this line produce compiler error.

We prevent creating an instance of this class by putting the constructor to private area. However we have a problem, according to the problem, it should be allowed to create one instance. If we mark the ctor ( constructor ) as private then we cant create any instance!

For solving this problem, we simply define a static boolean member "isAllowedToCreate" and a static member function "createInstance". Purpose is very easy to understand. createInstance function can access to private constructor because it is a class method. If isAllowedToCreate is true then the call to createInstance returns an instance of this class by calling the constructor inside the static function. In addition, isAllowedToCreate variable has to be assigned to false otherwise multiple calls to createInstance function returns multiple instances which is prohibited. It is certain that "isAllowedToCreate" member has to be put to the private area, otherwise programmers can reach it and change it to the true value.

So, lets look at the below code:

class CUnique{

int Id; //a private member of class

//We define contructor in private section so that nobody can access it
//from the outside of the class.
CUnique( int ID = 0 ): Id( ID ) {}

//we define a boolean variable for knowing if an instance is allowed to be created.
static bool isAllowedToCreate;

public:

//this static function is used to create an instance if there hasnt been created yet
static CUnique* createInstance( int ID = 0 ){

if( isAllowedToCreate ){
isAllowedToCreate = false;
return (new CUnique( ID ));
}

//if there is an existing instance then returns NULL
return static_cast< CUnique* >(0) ;
}

int getId() const { return this->Id; }
};

looks fine, isnt it? If isAllowedToCreate is true we can create an instance. But it has a serios problem! What if we delete the instance that we have created! There will be no object around but worse thing is that isAllowedToCreate has the value false! We can never create an instance again although it is legal! So make an action and write a destructor to this class.

//in destructor set the boolean variable true for creating new instance.
~CUnique(){
isAllowedToCreate = true;
}


Yes now it looks really fine. isnt it? We can create only one instance and if we delete this instance we can create another. However, you sholdnt believe to me because i am lying! We can create one instance it is true, but we can go further and create one another! Indeed we can create hundreds of objects which live together at the same time! Lets look at this:

CUnique *ptr1 , *ptr2;
ptr2 = CUnique::createInstance();
ptr1 = new CUnique( *ptr2 );

We simply call the copy constructor!! if we dont write a copy contructor, compiler will provide one for us which copy every single byte of one instance to anohter. However if we write a copy constructor compiler wont provide it for us. We should simply put the copy constructor to the private area like constructor! If anyone tries to use it, compiler will generate an error saying that you can not access a private member.

Our final class contains a copy constructor and destructor is:

class CUnique{

int Id; //a private member of class

//We define contructor in private section so that nobody can access it
//from the outside of the class.
CUnique( int ID = 0 ): Id( ID ) {}

//put copy constructor to private area!
CUnique( CUnique& ){}

//we define a boolean variable for knowing if an instance is allowed to be created.
static bool isAllowedToCreate;

public:

//this static function is used to create an instance if there hasnt created yet
static CUnique* createInstance( int ID = 0 ){

if( isAllowedToCreate ){
isAllowedToCreate = false;
return (new CUnique( ID ));
}

//if there is an existing instance then returns NULL
return static_cast<CUnique* >(0) ;
}

int getId() const { return this->Id; }

//in destructor set the boolean variable true for creating new instance.
~CUnique(){
isAllowedToCreate = true;
}
};

bool CUnique::isAllowedToCreate = true;

I write a test case, please read the comments and inspect the output:

int main(){

CUnique *ptr1 , *ptr2;

//create the first instance
ptr1 = ptr1->createInstance();

//this call returns NULL, because another instance is living
ptr2 = CUnique::createInstance();

cout << "ptr1's address:" << int ( ptr1 ) << endl ;
cout << "ptr2's address:" << int( ptr2 ) << endl ;

//delete the unique instance !!!!
delete ptr1;

//now this call returns an instance to ptr2 because there is no instance around.
ptr2 = CUnique::createInstance();
cout << "ptr2's address:" << int( ptr2 ) << endl ;

ptr1 = ptr1->createInstance();
cout << "ptr1's address:" << int( ptr1 ) << endl ;

return 0;
}


OUTPUT is:
ptr1's address:3362632
ptr2's address:0
ptr2's address:3362632
ptr1's address:0
Press any key to continue . . .


Dont forget that ptr2 = ptr1; is always a valid assignment but this doesnt mean there are multiple instances. Both pointers point the same object by this assignment.

Is this the final class? Can you crate an instance more than once with this class? I dont give the answers of these questions:) see you soon!

2 comments:

Unknown said...

Yeğen senin bu c/c++ konusundaki gayretini görünce, senin elinden tutmaya karar verdim.
1. Code Complete - Steve McConnell
2. Writing Secure Code - Michael Howard and David C. LeBlanc
3. Writing Solid Code Microsoft's Techniques for Developing Bug-Free C-Programs - Steve Maguire
4. Design Patterns - Elements Of Reusable Object Oriented Software - GoF

Öncelikli olarak bu kitapları oku. 2. si Nevinde olabilir MS den vermişlerdi.

Bitirince haber ver ikinci derse geçelim:))

emreknlk said...

Sağol balkan benim de elimden tutcak birine ihtiyacım vardı :) kitaplardan ilk ikisini okudum kısmen diğerlerine de kısmetse bakarim. 2.dersi merakla bekliyorum:)