Monday, April 6, 2009

Smart Pointer in C++

This topic will elaborate Smart Pointer concepts and move on to Reference Counting. There is a sample code to implement the same.

Background

Smart pointer or auto-pointer is a simple wrapper around a regular pointer, provides all meaningful operations to its embedded pointer (derefencing and indirection).

It's smartness lies in it's destructor which takes care of deleting it's embedded pointer and makes user not to worry from freeing programmer dynamically allocated memory.

The beauty of the logic behind designing Smart Pointer is that the Stack is safer than Heap. Variables on the Stack are automatically destructed when they go out of scope. Whereas Variables on Heap are freed only by an explicit call to Delete.

So, the trick is to represent Heap based object, accessed through a dumb pointer by a stack based object and when this stack based object goes out of scope, its Destructor is called, which in turn takes care of freeing the memory associated with the dumb pointer.

For Example here instead of writing

How smart pointer will take care of these types of programmer errors:

int main()

{

MyClass *p = new (MyClass);

MyClass *q = p;

p->DoSomething();

delete (p);

q->DoSomething(); // q is now a dangling pointer

}

When we assign pointer p to q as in the above code and then delete pointer p, it simply deletes the object pointed by pointer "p", irrespective of there is another pointer "q" referencing to it and then corrupts the heap.

There are set of solutions to this problem. But we can solve this in a simple way by providing reference counting facility to the embedded pointer.

Reference Counting does maintain a count of all the pointers that are referencing to the object and deletes the object only when this Reference Count becomes Zero or when the last object which is using this pointer gets destroyed.

Also you can use reference counting in any class that manages a shared resource, and not just for smart pointers or auto pointers.

Logic behind Reference Counting implementation is as below:

1. When the object that is being managed is created, its reference count
is set to one.

2. When the managing object - the smart pointer - is copied or assigned,
the reference count is incremented.

3. When a smart pointer is destructed, as when a member variable's owning
object is destructed, or a value goes out of scope, or an allocated
smart pointer is deleted, the reference count is decremented.

4. Once when the reference Count becomes zero, then the pointer gets deleted.

This is also called as Shared Ownership Pattern.

Here is a sample code which implements Smart Pointer with Reference Counting:

The code

Here class Pointer is a Real Pointer, where-as class Smart Pointer is a wrapper around Pointer.

#include

class Pointer

{

public:

//standard construction and destruction:

/*default constructor */

Pointer () {

cout<<"Pointer::default constructor = \n";

}

/*Copy constructot */

Pointer (const Pointer& rhs) {

cout<<"Pointer::copy constructor = \n";

myVal_ = rhs.myVal_;

}

/*Assignment operator overload */

Pointer& operator = (const Pointer& rhs) {

cout<<"Pointer::operator = \n";

if(this != &rhs)

{

myVal_ = rhs.myVal_;

return (*this);

}

}

/*Destructor */

~Pointer (void){

cout<<"Pointer::Destructor\n";

}

/*reference counting related methods */

void acquire(void){

++refCount_;

} /* increase refCount */

unsigned int release(void){

return(--refCount_);

} /*decrease refCount */

/*accessor*/

unsigned int count(void){

cout<<"Reference count value is ="<<<"\n";

return (refCount_); /*return current count */

}

private:

/* Class private data */

int myVal_;

unsigned int refCount_; /* reference counting related data */

}; /* End of Pointer class */

/****** Here is our own SmartPointer class for Pointer class ****** ***/

class SmartPointer {

public:

/* standard construction and destruction: */

/* Default construction: */

SmartPointer(Pointer *ptr):ptr_(ptr){

cout<<"SmartPointer::default constructor = \n";

ptr_->acquire();

}

/* Copy construction: */

SmartPointer(const SmartPointer &rhs):ptr_(rhs.ptr_){

cout<<"SmartPointer::copy constructor = \n";

ptr_->acquire();

}

/* Assignment operator: */

SmartPointer& operator = (const SmartPointer &rhs){

cout<<"SmartPointer::operator = \n";

if (this != &rhs)

{

ptr_->release();

ptr_ = rhs.ptr_;

ptr_->acquire();

return(*this);

}

}

/* Destructor: */

~SmartPointer (void){

cout<<"SmartPointer::Destructor\n";

if (NULL != ptr_)

{ if (0 == ptr_->release())

{

ptr_->count();

delete(ptr_);

ptr_ = NULL;

}

}

}

Pointer* operator -> (void) {

return (ptr_);

}

Pointer& operator * () const {

return(*ptr_);

}

private:

Pointer * ptr_;

}; // SmartPointer

// ******* client uses SmartPointer as a Stack Variable here ********

int main()

{

{

/* sp1 acquires refCount now. so refCount = 1 */

SmartPointer sp1 = SmartPointer( new Pointer());

sp1->count();

{

//sp2 referes sp1 and acquires refCount now. so refCount = 2

SmartPointer sp2 = sp1;

sp1->count();

} // at this point sp2 relases refCount. so refCount = 1

sp1->count();

} // at this point sp1 relases refCount. so refCount = 0 and so here the ptr_ gets deleted

return(0);

}

Friday, April 3, 2009

C++ Placement New operator

Abstract:


These are some notes I have on C++'s operator new.I will point out how to avoid using operator new at all. More general constructs in the language can achieve similar objectives with more flexibility. You may find the replacement allocator proposed here fairly disgusting. Just keep in mind that something far worse is built right into the language.

New operator Life cycle:
The new operator provides dynamic storage allocation.The new operator is used to find storage in free store for the object being created. The new expression returns a pointer to the object created and can be used to initialize the object. If the object is an array, a pointer to the initial element is returned.You can use set_new_handler() only to specify what new does when it fails.
When the object being created is an array, only the first dimension can be a general expression. All subsequent dimensions must be constant integral expressions. The first dimension can be a general expression even when an existing type is used. You can create an array with zero bounds with the new operator. For example:

char * pcharzero = new char[0];
In this case, a pointer to a unique object is returned.
If An object created with the help of operator new() or operator new[](). object exists until the operator delete() or operator delete[]() is called to deallocate the object's memory. A delete operator or a destructor will not be implicitly called for an object created with a new that has not been explicitly deallocated before the end of the program.

Placement New operator:
Operator new allocates memory from the heap, on which an object is constructed. Standard C++ also supports placement new operator(allocator function), which constructs an object on a pre-allocated buffer. This is useful when building a memory pool, a garbage collector or simply when performance and exception safety are paramount (there's no danger of allocation failure since the memory has already been allocated, and constructing an object on a pre-allocated buffer takes less time):

static const int MAX_SIZE = 10 ;

int* buffer = new int[MAX_SIZE] ;

inline void* operator new( size_t sz, void* buff )
{ return buff ; }
inline void* operator new[]( size_t sz, void* buff )
{ return buff ; }

for( int i = 0; i < MAX_SIZE; i++ )
{
int* p = new (buffer+i) int(i) ;
// specify where you want the object
}

Use of Placement:
Placement syntax has four main uses: default placement, preventing exceptions, custom allocators, and debugging.

Default Placement:
The placement overloads of operator new and operator delete that employ an additional void * parameter are used for default placement, also known as pointer placement.If you need a certain object at a specific hardware address use placement. It is also required for the construction of objects that need to reside in a certain memory area, such as an area that is shared between several processors of a multiprocessor computer.

Preventing exceptions:

Normally,the new functions throw an exception,if they encounter an error of type std::bad_alloc,such as exhaustion of all available memory.If you wish in your program not to throw an exception. Programmer must has to include the standard C++ library header in source code. For example:

typedef struct userobj
{
;
};
int main (void)
{
// Call the function operator new(size_t, const nothrow_t &) and (if successful) construct the object.
userobj * ptr = new (std::nothrow) userobj ;
if (ptr) {
// The storage has been allocated and the constructor called.
cout<<"storage has been allocated for ptr="<<<<"Allocation Error"<, programmers has to write their own allocation and deallocation functions, overloaded for user-defined types. For example, one could define a memory management class as follows:


class Allocator {
public:
void * allocate ( size_t ) ;
void deallocate ( void * ) ;
} ;

define custom placement allocation and deallocation functions as follows:

void *
operator new (size_t size, Allocator & arena)
{
return arena.allocate(size) ;
}
void
operator delete (void * ptr, Allocator & arena)
{
arena.deallocate(ptr) ;
}

The program would employ the placement syntax to allocate objects using different instances of the Allocator class as follows:

Allocator first_a, second_a ;
Type * p1 = new (first_a) Type ;
Type * p2 = new (second_a) Type ;

Destroying an object whose storage is allocated in such a fashion requires some care. Because there is no placement delete expression, one cannot use it to invoke the custom deallocator. One must either write a destruction function that invokes the custom deallocator, or call the placement delete function directly, as a function call.

The former would resemble:

void
destroy (Type * p, Allocator & arena)
{
p->~Type() ; // First invoke the destructor explicitly.
arena.deallocate(p) ; // Then call the deallocator function directly.
}

which would be invoked from a program as:

Allocator arena ;
Type * p = new (arena) Type ;
/* ... */
destroy(p, arena) ;

The latter would involve simply writing the destructor invocation and delete function call into the program:

Allocator arena ;
Type * p = new (arena) Type ;
/* ... */
p->~Type() ; // First invoke the destructor explicitly.
operator delete(p, arena) ; // Then call the deallocator function indirectly via operator delete(void *, Allocator &) .

A common error is to attempt to use a delete expression to delete the object. This results in the wrong operator delete function being called. Dewhurst recommends two strategies for avoiding this error. The first is to ensure that any custom allocators rely upon the Standard C++ library's global, non-placement, operator new, and are thus nothing more than simple wrappers around the C++ library's memory management. The second is to create new and delete functions for individual classes, and customize memory management via class function members rather than by using the placement syntax.

Debugging :

Placement new can also be used as a simple debugging tool, to enable programs to print the filename and line number of the source code where a memory allocation has failed. This does not require the inclusion of the Standard C++ library header , but does require the inclusion of a header that declares four placement functions and a macro replacement for the new keyword that is used in new expressions. For example, such a header would contain:

#if defined(DEBUG)
void * operator new (size_t size, const char* file, int line);
void * operator new[] (size_t size, const char* file, int line);
void operator delete (void * p, const char* file, int line);
void operator delete[] (void * p, const char* file, int line);
#define New new(__FILE__, __LINE__)
#else
#define New new
#endif

This would be employed in a program as follows:

Type * p = New Type ;

The custom-written placement new functions would then handle using the supplied file and line number information in the event of an exception. For example:

#include

class Error {
public:
Error(const char * file, int line) { /* ... */ }
/* ... */
} ;

void *
operator new (size_t size, const char* file, int line)
{
if (void * p = ::operator new (size, std::nothrow))
return p ;
throw Error(file, line) ;
}

Placement delete:

To avoid memory leak the placement delete functions are called.As noted above, there is no placement delete expression. It is not possible to call any placement operator delete function using a delete expression.

A placement new expression first calls the placement operator new function, then calls the constructor of the object upon the raw storage returned from the allocator function. If the constructor throws an exception, it is necessary to deallocate that storage before propagating the exception back to the code that executed the placement new expression, and that is the purpose of the placement delete functions.

The placement delete function that is called matches the placement new function that was invoked by the placement new expression. So, Example to implement placement delete fun which is called by operator delete(void *, const A &)


struct A {} ;
struct E {} ;

class Type {
public:
Type() { throw E() ; }
} ;

void * operator new ( size_t, const A & ) ;
void operator delete ( void *, const A & ) ;

int main ()
{
A a ;
Type * p = new (a) Type ;
return 0 ;
}

This is why the pointer placement delete functions are defined as no-operations by the Standard C++ library. Since the pointer placement new functions do not allocate any storage, there is no storage to be deallocated in the event of the object's constructor throwing an exception.[6]

If no matching placement delete function exists, no deallocation function is called in the event of an exception being thrown by a constructor within a placement new expression. There are also some (older) C++ implementations that do not support placement delete (which, like the exception-throwing allocator functions, were an addition made to C++ when it was standardized) at all. In both such situations, an exception being thrown by a constructor when allocating using a custom allocator will result in a memory leak.



Love Guru

Love is an emotion that has different connotations at different points of time. It is also a factor of age. Love as it is to a teenager is not the same as it is to a middle-aged person. Atal and Indira started late, but are collecting each moment as a precious gift, storing them in the fond cells of their minds. Their love is a sort of an example for young people, feels Jaya. She pays Indira a visit often, helping her with some chores now and then. Jaya told me how Atal helps Indira do the daily tasks. Indira has a chronic pain in the spine, a reminder of an accident she suffered as a child. The pain stiffens during winter. Then she faces difficulties in picking things up from the floor. That's where Atal helps her.