First of all, we need to understand the need of smart pointers. We have calloc/malloc and free in C and new and delete/delete[] in C++98. Smart programmers use them smartly, but a smart programmer can also make mistakes that can lead to memory leaks.
This is safe code, it give the assurance of freeing the allocated memory even exception happens (memory gets freed on stack unwinds).
Do not think that auto_ptr is smart and perfect, it also lacks in the situation following.
The same situation comes while passing auto_ptr as the function parameter. In this case also, ownership is transferred to function's receiving parameter and when the function ends, deletes the object as well.
Also auto_ptr can not be used to create the array of pointers of objects. means following creates the disaster.
As auto_ptr calls the delete internally not delete[], when it goes out of scope.
shared_ptr
I do not have any evidence but can say even Microsoft engineers (assuming they are very smart) also left memory leaks in Windows XP. I remember the days when I used to leave the Windows XP running for a week and seen XP becomes unresponsive. It was working fine after restarting the system.
Problem faced with traditional pointers:
- Forgot to delete object allocated at heap, or when exceptions happen (memory leak).
- Dangling pointers (more than one pointers are pointing to the same object and object is deleted by one the pointers.
- Managing lifetime of the pointers
C++98 introduced auto_ptr to manage naked pointers
When auto_ptr is assigned to another auto_ptr, it transfers the ownership of containing object as well. In the given example, autoPtr2 has the ownership of the object. When autoPtr2 goes out of scope, it deletes the object as well. Now autoPtr1 becomes the dangling pointer and accessing it leads to the crash.
try { auto_ptr<YourClass> autoPtr(new YourClass()); // write some code that can throw exception. (e.g. divide by zero) } catch (...) { }
This is safe code, it give the assurance of freeing the allocated memory even exception happens (memory gets freed on stack unwinds).
Do not think that auto_ptr is smart and perfect, it also lacks in the situation following.
class YourClass { public: int m_data = 10; }; int main() { auto_ptr<YourClass> autoPtr1(new YourClass()); { auto_ptr<YourClass> autoPtr2 = autoPtr1; } cout << autoPtr->m_data; return 0; }
The same situation comes while passing auto_ptr as the function parameter. In this case also, ownership is transferred to function's receiving parameter and when the function ends, deletes the object as well.
class YourClass { public: int m_data = 10; }; void TestFunction(auto_ptr<YourClass> autoPtr) { cout << autoPtr->m_data; } int main() { auto_ptr<YourClass> autoPtr(new YourClass()); TestFunction(autoPtr); cout << autoPtr->m_data; return 0; }
Also auto_ptr can not be used to create the array of pointers of objects. means following creates the disaster.
auto_ptr<YourClass> autoPtr(new YourClass[10]);
As auto_ptr calls the delete internally not delete[], when it goes out of scope.
C++11 introduced new set of pointers to solve the problem with naked pointer and auto_ptr
- shared_ptr
- unique_ptr
- weak_ptr
shared_ptr
shared_ptr works by implementing reference counting mechanism. When a shared_ptr is assigned to another shared_ptr, it shares the object instead of transferring the ownership like auto_ptr does. Also, it increases the reference count by 1. shared_ptr reference count is also called strong reference count.
As soon as any shared_ptr goes out of scope, reference count decrease by 1 and reference count becomes 0, object gets deleted.
you can create the shared_ptr to the array of objects:
To make sure that it calls delete[] instead of delete, we have to mention it via lambda.
Do you think that shared_ptr is perfect?
When sharedPtr2 goes out of scope, it deletes the object. When sharedPtr1 goes out of scope, it does not have anything to be deleted and hence you will get CRASH!!!.
So make a thumb rule, never create share_ptr from naked pointers.
Create shared_ptr only by the followings:
{ shared_ptr<YourClass> sharedPtr1(new YourClass); // reference count = 1 { shared_ptr<YourClass> sharedPtr2(sharedPtr1); // reference count = 2 { shared_ptr<YourClass> sharedPtr3 = sharedPtr2; // reference count = 3 } // reference count = 2 } // reference count = 1 } // reference count = 0 and objct gets deleted
As soon as any shared_ptr goes out of scope, reference count decrease by 1 and reference count becomes 0, object gets deleted.
you can create the shared_ptr to the array of objects:
shared_ptr<YourClass> sharedPtr(new YourClass[10], [](YourClass* ptr) { delete[] ptr; });
To make sure that it calls delete[] instead of delete, we have to mention it via lambda.
Do you think that shared_ptr is perfect?
No!!!, nothing is perfect in this world, so how can shared_ptr be?
consider the situation like:
int main() { YourClass* pYourClass = new YourClass(); shared_ptr<YourClass> sharedPtr1(pYourClass); // sharedPtr1 ref count == 1 shared_ptr<YourClass> sharedPtr2(pYourClass); // sharedPtr2 ref count == 2 return 0; }
When sharedPtr2 goes out of scope, it deletes the object. When sharedPtr1 goes out of scope, it does not have anything to be deleted and hence you will get CRASH!!!.
So make a thumb rule, never create share_ptr from naked pointers.
Create shared_ptr only by the followings:
shared_ptr<YourClass> sharedPtr1(new YourClass()); // or shared_ptr<YourClass> sharedPtr2 = make_shared<YourClass>();
Another issue with shared_ptr (cyclic dependency)
class First { public: shared_ptr<Second> m_sharedPtrSecond = nullptr; }; class Second { public: shared_ptr<First> m_sharedPtrFirst = nullptr; }; int main() { // Reference count of sharedPtrFirst == 1 shared_ptr<First> sharedPtrFirst(new First()); // Reference count of sharedPtrSecond == 1 shared_ptr<Second> sharedPtrSecond(new Second()); // Reference count of sharedPtrSecond == 2 sharedPtrFirst->m_sharedPtrSecond = sharedPtrSecond; // Reference count of sharedPtrFirst == 2 sharedPtrSecond->m_sharedPtrFirst = sharedPtrFirst; return 0; }
When main ends:
- sharedPtrSecond goes out of scope, it decreases the reference count by 1 and final reference count is still 1. The object of class Second does not gets deleted.
- Similarly when sharedPtrFirst goes out of scope, reference count is still 1 and object of class First does not get deleted.
We can see there are clear memory leaks in case of cyclic dependencies.
To solve this cyclic dependency problem, C++11 provides another smart pointer, called weak_ptr.
weak_ptr
Unlike shared_ptr, weak_ptr does not increase the string reference count but increases the special weak reference count. weak_ptr is created from shared_ptr. Assigning a weak pointer to another weak pointer increases the weak ref count.int main() { // strong ref count == 1 shared_ptr<YourClass> sharedPtr(new YourClass); // strong ref count == 1, weak ref count == 1 weak_ptr<YourClass> weakPtr1(sharedPtr); // strong ref count == 1, weak ref count == 2 weak_ptr<YourClass> weakPtr2 = weakPtr1; return 0; }
As it does not increase the strong ref count, it simply does not hold the ownership of the object.
If a shared_ptr(having strong ref count 1) goes out of scope, it simply deletes the object, no matters how many weak references still exists. All the weak_ptr, referring to this shared_ptr, become invalid.
Before using weak_ptr, you must check the validity of the existence using the expired() function.
(
To use weak_ptr, simply create shared_ptr from the weak_ptr by using the lock(). It increases the strong ref count by 1. Now while using it, we are assured about the existence of the object.
The object is destroyed and its memory deallocated when either of the following happens:
We can create the array of object and we do not need to provide the special delete instructions. It has the proper syntax for creating the array.
If a shared_ptr(having strong ref count 1) goes out of scope, it simply deletes the object, no matters how many weak references still exists. All the weak_ptr, referring to this shared_ptr, become invalid.
Before using weak_ptr, you must check the validity of the existence using the expired() function.
weak_ptr<YourClass> weakPtr; { shared_ptr<YourClass> sharedPtr(new YourClass); weakPtr = sharedPtr; } // weakPtr.expired() will return true here. if (weakPtr.expired() == false) { // safe code }
(
*
, ->
) are not allowed with weak_ptr unlike other pointers. As it is not the owner of the object and does not let the programme mishandle it. To use weak_ptr, simply create shared_ptr from the weak_ptr by using the lock(). It increases the strong ref count by 1. Now while using it, we are assured about the existence of the object.
int main() { weak_ptr<YourClass> weakPtr; shared_ptr<YourClass> sharedPtrFromWeakPtr; { shared_ptr<YourClass> sharedPtr(new YourClass); weakPtr = sharedPtr; sharedPtrFromWeakPtr = weakPtr.lock(); } // weakPtr.expired() will return false here if (weakPtr.expired() == false) { // safe code } return 0; }
Just see how cyclic dependency problem is resolved by weak_ptr
class Second; class First { public: weak_ptr<Second> m_weakPtrSecond; }; class Second { public: weak_ptr<First> m_weakPtrFirst; }; int main() { // strong ref count ==1 shared_ptr<First> sharedPtrFirst(new First); // strong ref count == 1 shared_ptr<Second> sharedPtrSecond(new Second); // strong ref count ==1, weak ref count ==1 sharedPtrFirst->m_weakPtrSecond = sharedPtrSecond; // strong ref count ==1, weak ref count ==1 sharedPtrSecond->m_weakPtrFirst = sharedPtrFirst; return 0; } // main ends // sharedPtrSecond goes out of scope, strong ref == 0 and weak ref == 1 // object gets deleted as strong ref ==0 // sharedPtrFirst goes out of scope, strong ref == 0 and weak ref == 1 // object gets deleted as strong ref ==0
unique_ptr
We can say it is the improvement over error prone auto_ptr. unique_ptr is the only owner of the object, no two unique_ptr should not point to the same object.
unique_ptr
managing the object is destroyedunique_ptr
managing the object is assigned another pointer via operator= or reset().
It gives the guarantee of releasing the memory always.
int main() { unique_ptr<YourClass> uniquePtr(new YourClass); // deletes the old YourClass object, // now uniquePtr points to new YourClass object uniquePtr = unique_ptr<YourClass>(new YourClass); // This is the another way to delete the YourClass object uniquePtr.reset(); return 0; }
int main() { std::unique_ptr<YourClass> uniquePtr1; { std::unique_ptr<YourClass> uniquePtr2(new YourClass); // uniquePtr1 = uniquePtr2; // Error ! can't copy unique_ptr uniquePtr1 = std::move(uniquePtr2); // YourClass instance will continue to live, // despite uniquePtr2 going out of scope } return 0; }
We can create the array of object and we do not need to provide the special delete instructions. It has the proper syntax for creating the array.
int main() { // When uniquePtr goes out of scope, calls the delete unique_ptr<YourClass> uniquePtr(new YourClass); // When uniquePtrArray goes out of scope, calls the delete[] unique_ptr<YourClass[]> uniquePtrArray(new YourClass[10]); return 0; }
For more reading:
http://www.umich.edu/~eecs381/handouts/C++11_smart_ptrs.pdf
http://en.cppreference.com/w/cpp/memory/auto_ptr
http://en.cppreference.com/w/cpp/memory/weak_ptr
http://en.cppreference.com/w/cpp/memory/shared_ptr
http://en.cppreference.com/w/cpp/memory/unique_ptr
Emoticon Emoticon