C++智能指针的使用

文章目录

智能指针的使用和原理

智能指针的使用场景

  1. 下面的程序中,new了以后,我们也delete了,但是因为抛异常导致后面的delete没有得到执行,所以就内存泄漏了,所以我们需要new以后捕获异常,捕获到异常后delete内存,再把异常抛出。
    2.但是因为new本身也可能抛异常,连续的两个new和下面的Divide都可能会抛异常,让我们处理起来很麻烦。智能指针放到这样的场景里面就让问题简单多了。
cpp 复制代码
double Divide(int a, int b)
{
	// 当b == 0时抛出异常
	if (b == 0)
	{
		throw "Divide by zero condition!";
	}
	else
	{
		return (double)a / (double)b;
	}
}

void Func()
{
	// 这⾥可以看到如果发⽣除0错误抛出异常,另外下⾯的array和array2没有得到释放。
	// 所以这⾥捕获异常后并不处理异常,异常还是交给外⾯处理,这⾥捕获了再重新抛出去。
	// 但是如果array2new的时候抛异常呢,就还需要套⼀层捕获释放逻辑,这⾥更好解决⽅案
	// 是智能指针,否则代码太戳了
	int* array1 = new int[10];
	int* array2 = new int[10];// 抛异常呢
	// 连续的两个new
	// 如果第一个new抛异常可以捕获
	// 如果第二个new抛异常,可以解决delete第二个new
	// 但是第一个new没有被delete
	// 这样就需要再第二个new的地方捕获异常,delete第一个new
	// 如果有很多个new呢,就太麻烦了,这里就需要智能指针了
	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; // 异常重新抛出,捕获到什么抛出什么
	}
	// ...
	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 << e.what() << endl;
	}
	catch (...)
	{
		cout << "未知异常" << endl;
	}

	return 0;
}

RAII和智能指针

  1. RAII是获取到资源立即初始化,它是一种管理资源的类的设计思想,本质是一种利用对象生命周期来管理获取到的动态资源,避免资源泄漏,这里的资源可以是内存、文件指针、网络连接、互斥锁等等。RAII在获取资源时把资源委托给一个对象 ,接着控制对资源的访问,资源在对象的生命周期内始终保持有效 ,最后在对象析构的时候释放资源,这样保障了资源的正常释放 ,避免资源泄漏问题。
  2. RAII像指针一样

在对象销毁后会正常的释放资源

cpp 复制代码
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)
{
	// 当b == 0时抛出异常
	if (b == 0)
	{
		throw "Divide by zero condition!";
	}
	else
	{
		return (double)a / (double)b;
	}
}

void Func()
{
	// 这⾥可以看到如果发⽣除0错误抛出异常,另外下⾯的array和array2没有得到释放。
	// 所以这⾥捕获异常后并不处理异常,异常还是交给外⾯处理,这⾥捕获了再重新抛出去。
	// 但是如果array2new的时候抛异常呢,就还需要套⼀层捕获释放逻辑,这⾥更好解决⽅案
	// 是智能指针,否则代码太戳了
	SmartPtr<int> p1 = new int[10];
	SmartPtr<int> p2 = new int[10];// 抛异常呢
	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 << e.what() << endl;
	}
	catch (...)
	{
		cout << "未知异常" << endl;
	}

	return 0;
}
  1. 智能指针类除了满足RAII的设计思路,还要方便资源的访问,所以智能指针类还像迭代器类一样
    重载 operator*/operator->/operator[] 等运算符,方便访问资源。
cpp 复制代码
template<class T>
class SmartPtr
{
public:
	// RAII
	SmartPtr(T* ptr)
		:_ptr(ptr)
	{}

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

	// 重载运算符,模拟指针的⾏为,⽅便访问资源
	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;
}

C++标准库智能指针的使用

浅拷贝的问题,共同管理同一块资源,而深拷贝是两个指针管理不同的资源了,现在要求两个指针管理同一块资源?

cpp 复制代码
int main()
{
	// 需要p1和p2同时管理同一块资源,浅拷贝
	// 析构多次的问题如何解决?
	SmartPtr<int> p1 = new int[10];
	SmartPtr<int> p2(p1);

	return 0;
}

1.C++标准库中的智能指针都在< memory >这个头文件下,智能指针有好几种,除了weak_ptr他们都符合RAII和像指针一样访问的行为,原理上而言主要是解决智能指针拷贝时的思路不同
2. C++98的智能指针
1、auto_ptr是C++98时设计出来的智能指针,它的特点是拷贝时把被拷贝对象的资源的管理权转移给拷贝对象 ,这是⼀个非常糟糕的设计,因为他会把被拷贝对象悬空(相当于被拷贝对象是空指针了),访问报错的问题,C++11设计出新的智能指针后,强烈建议不要使用auto_ptr

cpp 复制代码
struct Date
{
	int _year;
	int _month;
	int _day;

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

	// 本身不需要析构,为了验证auto_ptr是否可以解决析构两次的问题
	~Date()
	{
		cout << "~Date()" << endl;
	}
};

int main()
{
	// 拷⻉时,管理权限转移,被拷⻉对象ap1悬空
	auto_ptr<Date> ap1(new Date);

	auto_ptr<Date> ap2(ap1);
	// 空指针访问,ap1对象已经悬空
	// ap1->_year++;

	return 0;
}

C++11的智能指针
2、unique_ptr是唯一指针,他的特点是不支持拷贝,只支持移动。如果不需要拷贝的场景就非常建议使用它。把拷贝构造和拷贝赋值给(封)delete了

cpp 复制代码
int main()
{
   unique_ptr<Date> up1(new Date);

   不支持拷贝
   // unique_ptr<Date> up2(up1);

   支持移动,移动后up1也悬空了
   unique_ptr<Date> up3(move(up1));

   return 0;
}

3、shared_ptr共享指针,他的特点是支持拷贝,也支持移动 。如果需要拷贝的场景就需要使用他了 。底层是用引用计数的方式实现的。

cpp 复制代码
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弱(辅助解决shared_ptr的一个问题)指针,它不同于上面的指针,它不支持RAII,也就意味着不能用它直接管理资源,weak_ptr的产生本质是要解决shared_ptr的一个循环引用导致内存泄漏的问题

相关推荐
钟离墨笺3 小时前
【c++】四种类型转换形式
开发语言·c++
汤姆和杰瑞在瑞士吃糯米粑粑4 小时前
【C++学习篇】C++11
开发语言·c++
zjkzjk77114 小时前
C++ 左值引用 & 右值引用 && std::move()左值改右值 移动构造函数()
开发语言·c++
knowledgebao4 小时前
OHOS(HarmonyOS) 编译 C/C++ 库
c语言·c++·harmonyos
HL_LOVE_C5 小时前
全面理解-c++11中的智能指针
开发语言·c++
KAI77385 小时前
2月10日QT
c++
獨枭7 小时前
在 MFC 应用中集成 WebView2
c++·mfc
清泓y7 小时前
UE5--浅析委托原理(Delegate)
c++·ue5·ue4
RangoLei_Lzs8 小时前
C++性能优化—AI润色版
开发语言·c++·性能优化