1.使用std::shared_ptr避免函数异常退出而导致的内存泄漏
如下代码:
void funSharePtr(int a, int b)
{
std::shared_ptr<int> p(new int(10));
// 做一些操作
if (...) {
throw (std::exception("Error")); // 抛出异常且本函数没有捕获该异常
}
// ...其他操作
}
对于funSharePtr如果该函数异常退出,所有的局部变量都将被析构,因此智能指针p中指向的内存也会被智能指针释放掉
void funPtr(int a, int b)
{
int* p = new int(10);
// 做一些操作
if (...) {
throw (std::exception("Error")); // 抛出异常且本函数没有捕获该异常
}
// ...其他操作
delete p; // 释放p
}
对于funPtr,如果该函数异常退出,导致执行不到delete语句,发生内存泄漏
因此std::shared_ptr可以有效的避免因函数异常退出导致的内存泄漏。
2自定义删除器
对于一些c代码定义的结构体,没有为其定义自动释放资源的函数(析构函数),因此即使结构体退出作用域被释放掉后,资源也不一定被正确的释放掉:
struct Con connect(const char* addr); // 连接某个地址 并返回一个连接对象
void disconnect(Con* con); // 断开链接 释放连接资源(如套接字)
void f(const char* addr)
{
Con con = connect(addr);
// 使用con做一些事 ...
if (...) {
throw std::exception("Error"); // 抛出异常
}
disconnect(&con);
}
以上代码中,如果抛出异常,即使con是局部对象,函数异常退出也会被释放掉,但其并没有释放连接的资源,因为没有调用disconnect函数,此时同样会发生资源泄漏。
可以为std::shared_ptr自定义删除器替代delete操作,并进行资源释放的操作:
void f(const char* addr)
{
Con con = connect(addr);
// 定义了一个lambda作为删除器替代delete操作,参数类型要与p中指向的内存的类型一致
std::shared_ptr<Con> p(&con, [](Con* c) {disconnect(c); });
// 使用p做一些事 ...
if (...) {
throw std::exception("Error"); // 抛出异常
}
}
以上代码在正常退出或者异常退出时,均会执行disconnect函数,释放资源,此时std::shared_ptr并不会执行delete函数。
由此可见,对于栈上的变量,也可为其使用std::shared_ptr,但要确保其不会执行delete函数。
3关于智能指针的陷阱:
1.不要使用相同的普通指针来初始化多个std::shared_ptr
2.慎用get()函数
3.使用std::shared_ptr管理并非new出来的对象时,要指定删除器,以防止delete操作。