SoFunction
Updated on 2025-04-06

Use of C++ smart pointers

Use scenarios for smart pointers

1. In the following program, after new, we deleted it, but because the exception is thrown, the subsequent delete is not executed, so the memory leaks. Therefore, we need to catch the exception after new, delete the memory after catching the exception, and then throw the exception.
2. However, because the new itself may throw exceptions, the two consecutive new and the Divide below may throw exceptions, which makes it very troublesome to deal with. Putting smart pointers in such a scenario makes the problem much simpler.

double Divide(int a, int b)
{
	// When b == 0 exception is thrown	if (b == 0)
	{
		throw "Divide by zero condition!";
	}
	else
	{
		return (double)a / (double)b;
	}
}

void Func()
{
	// This can be seen that if an error except 0 is thrown, the following array and array2 are not released.	// So this does not handle the exception after catching the exception, and the exception is still left to the outside world to handle. This is caught and then thrown again.	// But if an exception is thrown when array2new, you still need to install a layer of capture and release logic, which is a better solution	// It's a smart pointer, otherwise the code will be too poked	int* array1 = new int[10];
	int* array2 = new int[10];// What about throwing an exception	// Two consecutive new	// If the first new throw exception can be caught	// If the second new throws an exception, you can solve the problem of delete the second new	// But the first new is not deleted	// In this way, you need to catch the exception in the second new place and delete the first new one	// If there are many new ones, it will be too troublesome, and smart pointers are needed here	try
	{
		int len, time;
		cin >> len >> time;
		cout << Divide(len, time) << endl;
	}
	catch (...)
	{
		cout << "delete []" << array1 << endl;
		cout << "delete []" << array2 << endl;
		delete[] array1;
		delete[] array2;
		throw; //Exception is re-throwed, what is caught and what is thrown	}
	// ...
	cout << "delete []" << array1 << endl;
	delete[] array1;
	cout << "delete []" << array2 << endl;
	delete[] array2;
}

int main()
{
	try
	{
		Func();
	}
	catch (const char* errmsg)
	{
		cout << errmsg << endl;
	}
	catch (const exception& e)
	{
		cout << () << endl;
	}
	catch (...)
	{
		cout << "Unknown exception" << endl;
	}

	return 0;
}

RAII and smart pointers

1. RAII is to obtain resources and is an instant initialization. It is a design idea for managing resources. Its essence is to use the object life cycle to manage the acquired dynamic resources to avoid resource leakage. The resources here can be memory, file pointers, network connections, mutex locks, etc.RAII delegates resources to an object when obtaining resources, then control access to resources,Resources remain valid throughout the life of an object, and finally release resources when object destruction, thus ensuring the resourceNormal release, avoid resource leakage problems.

2. RAII is like a pointer

Resources will be released normally after the object is destroyed

template<class T>
class SmartPtr
{
public:
	// RAII
	SmartPtr(T* ptr)
		:_ptr(ptr)
	{}

	~SmartPtr()
	{
		cout << "delete[] " << _ptr << endl;
		delete[] _ptr;
	}

private:
	T* _ptr;
};

double Divide(int a, int b)
{
	// When b == 0 exception is thrown	if (b == 0)
	{
		throw "Divide by zero condition!";
	}
	else
	{
		return (double)a / (double)b;
	}
}

void Func()
{
	// This can be seen that if an error except 0 is thrown, the following array and array2 are not released.	// So this does not handle the exception after catching the exception, and the exception is still left to the outside world to handle. This is caught and then thrown again.	// But if an exception is thrown when array2new, you still need to install a layer of capture and release logic, which is a better solution	// It's a smart pointer, otherwise the code will be too poked	SmartPtr<int> p1 = new int[10];
	SmartPtr<int> p2 = new int[10];// What about throwing an exception	SmartPtr<int> p3 = new int[10];

	int len, time;
	cin >> len >> time;
	cout << Divide(len, time) << endl;
}

int main()
{
	try
	{
		Func();
	}
	catch (const char* errmsg)
	{
		cout << errmsg << endl;
	}
	catch (const exception& e)
	{
		cout << () << endl;
	}
	catch (...)
	{
		cout << "Unknown exception" << endl;
	}

	return 0;
}

In addition to meeting the design ideas of RAII, the smart pointer class also needs to facilitate resource access, soSmart pointer class is like iterator classOverload operators such as operator*/operator->/operator[] to facilitate access to resources.

template<class T>
class SmartPtr
{
public:
	// RAII
	SmartPtr(T* ptr)
		:_ptr(ptr)
	{}

	~SmartPtr()
	{
		cout << "delete[] " << _ptr << endl;
		delete[] _ptr;
	}

	// Overload operators to simulate the behavior of pointers, facilitate access to resources	T& operator*()
	{
		return *_ptr;
	}

	T* operator->()
	{
		return _ptr;
	}

	T& operator[](size_t i)
	{
		return _ptr[i];
	}

private:
	T* _ptr;
};

void Func()
{
   p1[1] = 50;
   p4->first = 1;
   p4->second = 2;
   cout << p1[1] << endl;
}

Use of smart pointers in C++ standard library

The problem of shallow copy is to jointly manage the same resource, while deep copy is to manage different resources with two pointers. Now two pointers are required to manage the same resource?

int main()
{
	//P1 and p2 need to manage the same resource at the same time, and copy it	// How to solve the problem that has been destructed multiple times?	SmartPtr<int> p1 = new int[10];
	SmartPtr<int> p2(p1);

	return 0;
}

++The smart pointers in the standard library are all in the header file <memory>. There are several smart pointers. Except for weak_ptr, they all conform to RAII and access behaviors like pointers. In principle, they are mainly used to solve the smart pointer.Different ideas when copying
2. C++98 smart pointer

auto_ptr is a smart pointer designed in C++98.Its characteristic is that when copying, the management rights of the copied object's resources are transferred to the copied object.This is a very bad design.Because he will hang the copied object in the air(It is equivalent to the copy object being null pointer). After the access error is reported, after C++11 designs a new smart pointer, it is strongly recommended not to use auto_ptr

struct Date
{
	int _year;
	int _month;
	int _day;

	Date(int year = 1, int month = 1, int day = 1)
		:_year(year)
		, _month(month)
		, _day(day)
	{}

	// Destruction is not required by itself. In order to verify whether auto_ptr can solve the problem of destruction twice	~Date()
	{
		cout &lt;&lt; "~Date()" &lt;&lt; endl;
	}
};

int main()
{
	// When copying, the management permissions are transferred, and the copied object ap1 is hung empty	auto_ptr&lt;Date&gt; ap1(new Date);

	auto_ptr&lt;Date&gt; ap2(ap1);
	// Access the null pointer, the ap1 object is already empty	// ap1-&gt;_year++;

	return 0;
}

C++11 smart pointer

2. Unique_ptr is the only pointer.Its characteristic is that it does not support copying, only supports mobile. It is highly recommended if you don't need a copy. Assign copy constructs and copy values ​​to (block) delete

int main()
{
   unique_ptr&lt;Date&gt; up1(new Date);

   Copying is not supported
   // unique_ptr&lt;Date&gt; up2(up1);

   Support mobile,After movingup1Also hanging
   unique_ptr&lt;Date&gt; up3(move(up1));

   return 0;
}

3. shared_ptr shared pointer, its characteristics areSupports copying and also supports mobile. If neededHe needs to be used for copying scenes. The bottom layer is usedReference counting methodof.

int main()
{
   shared_ptr<Date> p1(new Date);
   shared_ptr<Date> p2(p1);
   shared_ptr<Date> p3(p1);
   
   cout << p1.use_count() << endl;// 3
   p1->_day++;
   cout << p1->_day << endl; // 2
   cout << p2->_day << endl; // 2
   cout << p3->_day << endl; // 2
   
   return 0;
}

4. Weak_ptr weak (assisted in solving a problem with shared_ptr) pointer, which is different from the above pointer. It does not support RAII, which means that it cannot be used to directly manage resources.The essence of weak_ptr generation is to solve the problem of memory leakage caused by a circular reference of shared_ptr.

This is the end of this article about the use of C++ smart pointers. For more related C++ smart pointers, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!