Code360 powered by Coding Ninjas X Naukri.com. Code360 powered by Coding Ninjas X Naukri.com
Table of contents
1.
Introduction
2.
Smart pointers
2.1.
auto_ptr
2.2.
unique_ptr
2.3.
shared_ptr
2.4.
weak_ptr
3.
Frequently Asked Questions
3.1.
Why is auto_ptr deprecated?
3.2.
In what scenarios can we use unique_ptr?
3.3.
What are the two kinds of pointer shared_ptr may relate to?
4.
Conclusion
Last Updated: Mar 27, 2024

auto_ptr unique_ptr shared_ptr and weak_ptr

Author Yashesvinee V
1 upvote
Crack Google SDE interview : Essential projects
Speaker
Saurav Prateek
SDE-2 @
20 Jun, 2024 @ 01:30 PM

Introduction

The concept of pointers is used for accessing the resources external to the program or the memory. Any variable or resource we use could be just a copy of the resource. The changes we make to it affect only the copied version and not the original one. A pointer can store the original resource's location for the change to be reflected. C and C++ use pointers to perform other operations such as allocating new objects on the heap, passing functions to other functions and iterating over the elements of data structures. Also see, Literals in C.Fibonacci Series in C++

Smart pointers

In C/C++, deallocating a pointer can cause a memory leak that may crash the program. A special category of pointers ensures that programs are free of memory and resource leaks and are safe from exceptions. To deallocate unused memory and free the destroyed object’s memory, Smart pointers are used. In this blog, we shall discuss the different types of Smart pointers in C++.

  1. auto_ptr (deprecated)
     
  2. unique_ptr
     
  3. shared_ptr
     
  4. weak_ptr
     

These pointers are defined in the std namespace and the “memory” header file.

auto_ptr

The auto_ptr class template is deprecated as of C++11. It is similar to unique_ptr, which has improved security, array support and other additional features. auto_ptr provides a limited garbage collection facility for pointers. It allows pointers to automatically destroy the elements when the object itself is destroyed.

If two auto_ptr objects happen to own the same element, both will attempt to destroy them at some point. Auto_ptr is based on an exclusive ownership model where two pointers of the same type can’t point to the same resource simultaneously. It transfers ownership of the stored value assigned to another object.

auto_ptr

The below example shows two auto_ptr of the same type. Initially, x holds an address which is later copied to y. This causes x to become Null. auto_ptr y, which now holds the address, is passed as an object to a function named funcA. When the function returns, the contents of y are also changed to NULL. 

The get() function returns a standard pointer to an object based on the auto_ptr.

#include <iostream>
#include <memory>
using namespace std;

class A 
{
	public:
		void display() { }
};

void funcA(auto_ptr<A> x) { }

int main()
{
	// x is an auto_ptr of type A
	auto_ptr<A> x(new A);
	cout << "x" << endl;
	
	// return address of x
	cout << "Before copy " << x.get() << endl;

	// copying makes x empty.
	auto_ptr<A> y(x);

	// x is empty now
	cout << "After copy " << x.get() << endl;
	cout << "y"<< endl;
	
	// x gets copied in y
	cout << "Before func call " << y.get() << endl;
  
    // function call to pass y
	funcA(y);
  
	// y is now empty
	cout << "After func call " << y.get() << endl;

	return 0;
}

Output:

x
Before copy 0x15ece70
After copy 0
y
Before func call 0x15ece70
After func call 0

This shows that two auto_ptr objects will not try to delete the same pointer. Deleting a pointer twice is not a valid operation and can crash the program, whereas deleting NULL is valid and possible.

unique_ptr

The unique_ptr is a smart pointer that owns and manages another object via a pointer. It disposes of the object using the associated deleter when unique_ptr goes out of scope. Out of scope means that either the unique_ptr object is destroyed or is assigned another pointer. The default deleter destroys the object and deallocates the memory using the delete operator. As mentioned before, it is an improved version of auto_ptr. However, unlike auto_ptr, unique_ptr can be transferred but not copied or shared.

Unique_ptr can manage a single object or a dynamically-allocated array of objects. It can quickly store and retrieve data from Collections in C++. Usually, it is used to manage the lifetime of objects. This may include acquiring and passing ownership of objects with dynamic lifetime into functions and providing safety from exceptions. It guarantees deletion on normal exits and exits due to exceptions.

Declaration:

unique_ptr<T> ptr(raw);

where,

ptr – pointer of unique_ptr type,

raw – raw pointer to type T,

T is the type of value pointed to by ptr.

It has three main functions:

  • get() returns a standard pointer to an object based on the unique_ptr pointer without transferring its ownership.
     
  • release() returns a standard pointer based on the unique_ptr pointer with the transfer of ownership.
     
  • reset() resets the ownership of the object. The unique_ptr pointer's value is changed to NULL.
     

In the below example, two unique_ptr are of the same type. Initially, x holds an address which is later transferred to y. This causes x to become Null. unique_ptr y now holds the address. A function fun containing a new unique_ptr is called and returned. The returned value is then transferred to y.

#include <iostream>
#include <memory>
using namespace std;

class A 
{
	public:
		void display() {}
};

unique_ptr<A> fun()
{
	unique_ptr<A> ptr(new A);
	cout << "ptr: " << ptr.get() << endl;
	return ptr;
}

int main()
{
	unique_ptr<A> x(new A);
	cout << "x" << endl;
  
	// returns address of a
	cout << "Before Transfer" << x.get() << endl;

	// transferring ownership
	unique_ptr<A> y = move(x);
  
	// x is empty
	cout << "After Transfer " << x.get() << endl;
	cout << "y" << endl;
	cout << "Before Call " << y.get() << endl;

	// calling fun()
	y = move(fun());
	cout << "After call " << y.get() << endl;

	return 0;
}

Output:

x
Before Transfer 0xb51e70
After Transfer 0
y
Before call 0xb51e70
ptr 0xb52ea0
After call 0xb52ea0

shared_ptr

The shared_ptr pointer shares the ownership of an object while storing a pointer to another object. It uses the reference counting ownership model, which maintains the reference count of the pointer with all copies of the shared_ptr. Reference counting is a way of sorting the number of references, disk space, memory block and other resources. A counter is incremented every time a new pointer points to the resource and decremented when the destructor of an object is called.

An object referenced by the raw pointer is not destroyed until the reference count exceeds zero. A reference count greater than zero implies that all copies of shared_ptr have been deleted.

Shared_ptr is used to point to member objects while owning the object they belong to. Multiple threads on different instances of shared_ptr can be called without additional synchronisation, even if the instances are copies.

Declaration:

shared_ptr<T> ptr;
shared_ptr<T> ptr(raw);

where

ptr refers to the name of the shared_ptr,

T denotes the data type of the pointer it points to,

raw is a pointer to data of type T.

The methods supported by shared_ptr pointer:

  • get() returns a pointer based on the shared_ptr pointer without transferring rights to the object. 
     
  • swap() swaps the values of two pointers.
     
  • reset() resets the ownership of the object.
     
  • use_count() returns the number of copies of the object pointers.
     

The below example shows two auto_ptr of the same type. Initially, x holds an address which is later shared with y. The number of copies of their object pointers is calculated. This returns 2 as there are two copies of the pointer. X is then reset and becomes empty. Now, the number of copies of object pointers reduces to 1.

#include <iostream>
#include <memory>
using namespace std;

class A 
{
	public:
		void display() {}
};

int main()
{
	shared_ptr<A> x(new A);
	cout << "x Before share " << x.get() << endl;

    // sharing
	shared_ptr<A> y(x);

	cout << "x After share " << x.get() << endl;
	cout << "y After share " << y.get() << endl;

	// Return the number of shared_ptr objects
	cout << "x no of object pointers " << x.use_count() << endl;
	cout << "y no of object pointers" << y.use_count() << endl;

	// Reset ownership of x
	x.reset();
	cout << "x After reset " << x.get() << endl;
	cout << "y After reset no of object pointers" << y.use_count() << endl;
	cout << "y After reset " << y.get() << endl;

	return 0;
}

Output:

x Before share 0x10e6e70
x After share 0x10e6e70
y After share 0x10e6e70
x no of object pointers 2
y no of object pointers 2
x After reset 0
y After reset no of object pointers 1
y After reset 0x10e6e70

 

You practice by yourself with the help of online c++ compiler.

weak_ptr

weak_ptr holds a weak reference to an object managed by the shared_ptr. It must be converted to shared_ptr to access the referenced object. Thus, weak_ptr is created as a copy of shared_ptrweak_ptr implements temporary ownership and does not own the object it points to. It tracks the object, which is then converted to shared_ptr to assume temporary ownership. 

weak_ptr can also break reference cycles that are formed by the objects managed by shared_ptr. This helps solve Cyclic Dependency Problems with shared_ptr. If two classes A and B point to each other using a shared_ptr, the use_count will never reach zero, and they never get deleted. weak_ptr can prevent this problem as they are not reference counted. The shared_ptr of class A can be declared as a weak_ptr declaring A_ptr as weak_ptr. Hence, class A does not own the object and can only access it.

To change the object a weak_ptr pointer points to, it is converted into a shared_ptr pointer using the lock() method. If the original shared_ptr gets destroyed, then the object's lifetime continues until the temporary shared_ptr is destroyed.

Declaration:

shared_ptr<T> shared(raw);
weak_ptr<T> weak = shared;

where

raw – a raw pointer pointing to data of type T,

shared – a pointer of shared_ptr type,

weak – a pointer of weak_ptr typ,

T – data type pointed to by raw, shared, weak pointers.

The methods defined for weak_ptr are:

  • lock() converts a pointer of weak_ptr type into shared_ptr type.
     
  • swap() swaps weak_ptr type pointers.
     

In the following example, two shared_ptrs are created along with two weak_ptrs. The weak_ptrs are then swapped with each other to acquire a resource. Next, we count the number of elements weak_ptr can access. Initially, weak_ptr_1 can access only the object pointed by shared_ptr_2. After the creation of shared_ptr_3, using shared_ptr_2, weak_ptr can now access two object pointers. 

#include <memory>
#include <iostream>
int main()
{
    // shared_ptr 
    std::shared_ptr<int> shrd_ptr_1(new int(8));
    std::shared_ptr<int> shrd_ptr_2(new int(10));
    
    // weak_ptr
    std::weak_ptr<int> wk_ptr_1(shrd_ptr_1);
    std::weak_ptr<int> wk_ptr_2(shrd_ptr_2);
 
    std::cout << "Before swap wk_ptr_1 = " << *wk_ptr_1.lock() << std::endl;
  
    std::cout << "Before swap wk_ptr_2 = " << *wk_ptr_2.lock() << std::endl;

    // swapping weak_ptr_1 and weak_ptr_2
    swap(wk_ptr_1, wk_ptr_2);
    std::cout << " After swap wk_ptr_1 = " << *wk_ptr_1.lock() << std::endl;
  
    std::cout << "After swap wk_ptr_2 = " << *wk_ptr_2.lock() << std::endl;

    std::cout << "Initial count the number_of_weak_ptr : " << wk_ptr_1.use_count() << std::endl;

    std::shared_ptr<int> shrd_ptr_3(shrd_ptr_2);
    
    std::cout << "Final count the number_of_weak_ptr : " << wk_ptr_1.use_count() << std::endl;
    return (0);
}

Output:

Before swap wk_ptr_1 = 8
Before swap wk_ptr_2 = 10
After swap wk_ptr_1 = 10
After swap wk_ptr_2 = 8
Initial count the numner_of_weak_ptr : 1
Final count the number_of_weak_ptr : 2
Get the tech career you deserve, faster!
Connect with our expert counsellors to understand how to hack your way to success
User rating 4.7/5
1:1 doubt support
95% placement record
Akash Pal
Senior Software Engineer
326% Hike After Job Bootcamp
Himanshu Gusain
Programmer Analyst
32 LPA After Job Bootcamp
After Job
Bootcamp

Frequently Asked Questions

Why is auto_ptr deprecated?

Since no two pointers should contain the same object, it transfers ownership if the stored value is assigned to another object. This resets the auto pointer to a null pointer. They can’t be used within STL containers due to the inability to be copied.

In what scenarios can we use unique_ptr?

unique_ptr can be used when you want to have single ownership of the resource. Only one unique_ptr can point to a resource. Hence, its not possible to copy one unique_ptr to another.

What are the two kinds of pointer shared_ptr may relate to?

Shared_ptrs can relate to a stored pointer and an owned pointer. A stored pointer is said to point to an object and can dereference it using an operator. An owned pointer is a part of the ownership group that is in charge of deleting the object it owns at some point.

Conclusion

This article has extensively discussed auto_ptr, unique_ptr, shared_ptr and weak_ptr with examples. It focuses on their working and implementation in detail.

Feeling curious? Coding Ninjas has you covered. Check out our articles on Pointer in C++Difference between Pointers and References and  Smart Pointer in C++Follow our Guided path for C++ programming here.

Recommended Reading:

Difference Between Structure and Union

Explore our Library on Coding Ninjas Studio to gain knowledge on Data Structures and AlgorithmsMachine LearningDeep Learning, and many more! Test your coding skills by solving our test series and participating in the contests hosted on Coding Ninjas Studio! 

Looking for questions from tech giants like Amazon, Microsoft, Uber, etc.? Look at the problems, interview experiences, and interview bundle for placement preparations. Upvote our blogs if you find them insightful and engaging! Happy Coding! Upvote our blogs if you find them insightful and engaging! Happy Coding!

 


 

Previous article
Delete this in C++
Next article
Dangling, Void, Null and Wild Pointers
Live masterclass