Unique_ptr() in C++
In this article, you will learn about the unique_ptr() in C++ with its examples.
Unique ptr():
A smart pointer called std::unique_ptr in C++ manages memory for dynamically allocated objects in an automated fashion. It was included in C++11 to address many of the problems with manual memory management using raw pointers and new and delete operations. It is a component of the C++ Standard Library.
It can be used the same way as a regular pointer because the -> and * operators on unique_ptr are overloaded.
The following are some of the essential qualities and traits of std::unique_ptr:
Unique Ownership:
- Std::unique_ptr's use of the word "unique" denotes that it is the only party owning the dynamically allocated object. In other words, only one std::unique_ptr can be the owner of a specific object at any one time. The associated object is immediately deleted when the std::unique_ptr exits scope or is explicitly reset, eliminating memory leaks.
Automatic cleanup:
- A std::unique_ptr is invoked after it has exited scope.
- It ensures that when resources are no longer required, they are released automatically.
Move Semantics:
- Move semantics allows for the transfer of ownership of a std::unique_ptr.
- As a result, you can essentially move ownership of an object from one std::unique_ptr to another, which also effectively transfers management of the object.
No Overhead:
- Compared to raw pointers, std::unique_ptr is made with a low overhead in mind.
- It has no adverse effects on runtime performance or memory use.
Special Deleters:
- When making a std::unique_ptr, you can supply unique deleter functions or function objects.
- It is helpful when carrying out more complex cleanup procedures than just deleting.
- Compatibility with Common Algorithms Standard algorithms and containers can be utilized with std::unique_ptr, making it simple to interact with them securely and effectively.
Avoids Dangling Pointers:
- Std::unique_ptr helps to avoid problems with pointers that point to memory locations that have already been deallocated because ownership is unique.
std::unique_ptr<int> ptr1 = std::make_unique<int>(42);
std::unique_ptr<int> ptr2 = std::move(ptr1); // Ownership transferred to ptr2, ptr1 is now null
Example 1:
#include <memory>
int main() {
// Create a unique_ptr and allocate an integer
std::unique_ptr<int>uniquePtr = std::make_unique<int>(42);
// Use uniquePtr as if it were a raw pointer
int value = *uniquePtr;
// ...
// Automatically release the Memory when uniquePtr goes out of scope
return 0;
} //Memory is automatically freed here
Explanation:
- In this example, a std::unique_ptr that owns an integer with the value 42 is created using the std::make_unique function.
- The integer is automatically destroyed when uniquePtr exits the scope.
- A crucial component of contemporary C++ programming is std::unique_ptr, which offers a clear ownership model for dynamically allocated objects, enhancing resource management and code safety.
Example:
#include <iostream>
#include <memory>
struct Task
{
int mid;
Task(int id ) :mId(id)
{
std::cout<<"Task::Constructor"<<std::endl;
}
~Task()
{
std::cout<<"Task::Destructor"<<std::endl;
}
};
int main()
{
// Create a unique_ptr object through raw pointer
std::unique_ptr<Task>taskPtr(new Task(23));
//Access the element through unique_ptr
int id = taskPtr->mId;
std::cout<<id<<std::endl;
return 0;
}
Output:
![Unique_ptr() in C++](https://static.tutorialandexample.com/cpp/unique-ptr-in-cpp1.png)
- The taskPtr object of type unique_ptrTask> accepts a raw pointer as a parameter.
- This object will now escape the function's scope upon exit, and its destructor will be called.
- The related raw pointer is deleted by taskPtr's destructor for the unique_ptr object.
- Therefore, taskPtr's destructor will always be executed, whether a function exits normally or abnormally (due to an exception).
- Therefore, the raw pointer will always be removed, stopping the memory leak.
Releasing the associated raw pointer:
- The ownership of the related raw pointer is released from the object when release() is called on a unique_ptr object.
- It gives back a raw pointer.
Changing the object's ownership to unique_ptr:
- A unique_ptr object can be moved, but it cannot be copied. It denotes that a unique_ptr object can transfer ownership of a raw pointer associated with it to another unique_ptr object.
- unique_ptr object is not copyable
The object unique_ptr cannot be copied:
- Unique_ptr is exclusively movable; it cannot be copied. As a result, neither the copy constructor nor the assignment operator may be used to duplicate a unique_ptr object.
Resetting a unique_ptr:
- A unique_ptr can be reset by calling the reset() function. Doing so removes the associated raw pointer, leaving the unique_ptr object empty.
Example 2:
#include <iostream>
#include <memory>
struct Task
{
int mid;
Task(int id ) :mId(id)
{
std::cout<<"Task::Constructor"<<std::endl;
}
~Task()
{
std::cout<<"Task::Destructor"<<std::endl;
}
};
int main()
{
// Empty unique_ptr object
std::unique_ptr<int> ptr1;
// Check if the unique pointer object is empty
if(!ptr1)
std::cout<<"ptr1 is empty"<<std::endl;
// Check if the unique pointer object is empty
if(ptr1 == nullptr)
std::cout<<"ptr1 is empty"<<std::endl;
// can not create unique_ptr object by initializing through assignment
// std::unique_ptr<Task> taskPtr2 = new Task(); // Compile Error
// Create a unique_ptr object through raw pointer
std::unique_ptr<Task>taskPtr(new Task(23));
// Check if taskPtr is empty or if it has an associated raw pointer
if(taskPtr != nullptr)
std::cout<<"taskPtr is not empty"<<std::endl;
//Access the element through unique_ptr
std::cout<<taskPtr->mId<<std::endl;
std::cout<<"Reset the taskPtr"<<std::endl;
// Reseting the unique_ptr will delete the associated
// raw pointer and make unique_ptr object empty
taskPtr.reset();
// Check if taskPtr is empty or if it has an associated raw pointer
if(taskPtr == nullptr)
std::cout<<"taskPtr is empty"<<std::endl;
// Create a unique_ptr object through raw pointer
std::unique_ptr<Task> taskPtr2(new Task(55));
if(taskPtr2 != nullptr)
std::cout<<"taskPtr2 is not empty"<<std::endl;
// unique_ptr object is Not copyable
//taskPtr = taskPtr2; //compile error
// unique_ptr object is Not copyable
//std::unique_ptr<Task> taskPtr3 = taskPtr2;
{
// Transfer the ownership
std::unique_ptr<Task> taskPtr4 = std::move(taskPtr2);
if(taskPtr2 == nullptr)
std::cout<<"taskPtr2 is empty"<<std::endl;
// ownership of taskPtr2 is transferred to taskPtr4
if(taskPtr4 != nullptr)
std::cout<<"taskPtr4 is not empty"<<std::endl;
std::cout<<taskPtr4->mId<<std::endl;
//taskPtr4 goes out of scope and deletes the associated raw pointer
}
// Create a unique_ptr object through raw pointer
std::unique_ptr<Task> taskPtr5(new Task(55));
if(taskPtr5 != nullptr)
std::cout<<"taskPtr5 is not empty"<<std::endl;
// Release the ownership of the object from the raw pointer
Task * ptr = taskPtr5.release();
if(taskPtr5 == nullptr)
std::cout<<"taskPtr5 is empty"<<std::endl;
std::cout<<ptr->mId<<std::endl;
delete ptr;
return 0;
}
Output:
![Unique_ptr() in C++](https://static.tutorialandexample.com/cpp/unique-ptr-in-cpp2.png)