Introduction
Now we have new C++ standards with us. The new standards have taken into account some of the real good features which were missing in C++. But this article has nothing to do with C++11. perhaps my future articles will talk about them, but this article talks about the smart pointers and contains a rudimentary implementation of "auto_ptr".
Background
One of the major benefits of using C++ is its deterministic nature. Using C++, we can deterministically control our memory allocation and deallocation (new and delete). But sometimes this becomes a problem. If a novice programmer forgets to delete any object created by new, then we have a memory leak. In case of long running systems, even small memory leaks will create bigger problems. So how can we ensure that for every new, there is a corresponding delete.
One way to do so is to use auto_ptr available in C++. auto_ptr transfers the ownership on copy operations, but if we need shared ownership then perhaps we can use boost libraries' smart pointers. But to really understand how these things must be working internally, we need to first implement them. Implementing smart pointers gives us the full understanding of why we need smart pointers, how they are working underneath and where to look for if we have some bug. This exercise here is an implementation of auto_ptr. The main idea is to understand how auto_ptr takes care of transfer of ownership and using various other operations.
Using the Code
The project contains two main classes. Our implementation of smart pointer is in MyAuto_Ptr and other class to test this smart pointer MyTestObject. The test suite is written in the main class.
I have put in comments in all the places where we need to know what that code is doing so without talking any more, I give you the code.
The Test Class
class MyTestObject
{
string name;
int age;
public:
MyTestObject(string s, int a)
{
name = s;
age = a;
cout << "Object created\n";
}
~MyTestObject(void)
{
cout << "Object destroyed\n";
}
string GetName()
{
return name;
}
int GetAge()
{
return age;
}
};
Our Smart Pointer
template <class T>
class MyAuto_Ptr
{
private:
T *ptr;
public:
explicit MyAuto_Ptr(T *p = 0)
:ptr(p)
{
}
~MyAuto_Ptr(void)
{
delete ptr;
}
MyAuto_Ptr(MyAuto_Ptr<T> &source)
{
ptr = source.ptr;
source.ptr = 0;
}
MyAuto_Ptr& operator=(MyAuto_Ptr<T> &source)
{
if(ptr != source.ptr)
{
delete ptr;
ptr = 0;
ptr = source.ptr;
source.ptr = 0;
return *this;
}
}
T& operator *()
{
return *ptr;
}
T* operator ->()
{
return ptr;
}
T* get()
{
return ptr;
}
void reset(T *newVal)
{
delete ptr;
ptr = newVal;
}
T* release()
{
T *t = ptr;
ptr = 0;
return t;
}
};
Testing the Class (Main)
int main(int argc, char* argv[])
{
{ MyAuto_Ptr<MyTestObject> p(new MyTestObject("Rahul Singh", 29));
cout << "Name: " << p->GetName() << ", Age: " << p->GetAge() << "\n";
MyAuto_Ptr<MyTestObject> p2(p);
cout << "Name: " << (*p2).GetName() << ", Age: " << (*p2).GetAge() << "\n";
MyAuto_Ptr<MyTestObject> p3;
p3 = p2;
cout << "Name: " << p3.get()->GetName() << ",
Age: " << p3.get()->GetAge() << "\n";
p3.reset(new MyTestObject("Megha Singh", 28));
cout << "Name: " << p3.get()->GetName() << ",
Age: " << p3.get()->GetAge() << "\n";
MyAuto_Ptr<MyTestObject> p4(p3.release());
cout << "Name: " << p4.get()->GetName() << ",
Age: " << p4.get()->GetAge() << "\n";
}
getchar();
return 0;
}
NOTE: Please refer to the source code for implementation details.
Points of Interest
This exercise is something I did long ago when one of my mentors (also best of all) told me that the best way to learn something is to get your hands dirty and just do it. Since I wrote this in the very beginning of my career (almost 6 years ago), it was very helpful in understanding the basics. I suggest that all new C++ programmers should do the same exercise and it will make them understand things better.
History
MyAuto_Ptr: Implementation of auto_ptr
- Changed the Implementation as suggested in the comments (Thanks for the valuable inputs)
- ToDo:
MySharedAuto_Ptr: Implementation of shared_ptr (I will post that soon)