以对象管理资源:智能指针

以对象管理资源

如果直接使用delete对资源进行释放,会因异常情况导致控制流不会到达delete语句而无法完成资源释放。我们利用对象生命周期结束自动析构这一点,将资源生命周期和对象生命周期进行绑定,从而确保资源正确释放。这类用于绑定资源生命周期的类叫资源管理类。管理内存对象的资源管理类通常是智能指针。

智能指针

  1. unique_ptr
#include <iostream>
#include <memory>

int main() {
    std::unique_ptr<int> pointer(new int(4));
    std::cout << *pointer << std::endl;
}

unique_ptr可以管理一个对象的资源,但是不允许对其进行复制。你可以使用move移动其所有权。

std::unique_ptr<int> pointer2(std::move(pointer));
  1. shared_ptr

由于unique_ptr不允许复制,但是复制的需求确实存在,所以出现了引用计数智能指针shared_ptr

shared_ptr是允许复制和赋值的,这类操作会使得shared_ptr计数增加,当最后一个shared_ptr被销毁时(即计数降为0时),它才会释放所管理的资源。

#include <iostream>
#include <memory>

int main() {
    std::shared_ptr<int> ptr1(new int(5));
    std::cout << "ptr1: " << *ptr1 << ", use_count: " << ptr1.use_count() << std::endl;

    {
        std::shared_ptr<int> ptr2 = ptr1;
        std::cout << "ptr2: " << *ptr2 << ", use_count: " << ptr2.use_count() << std::endl;
    }

    std::cout << "ptr1: " << *ptr1 << ", use_count: " << ptr1.use_count() << std::endl;

    return 0;
}
智能指针的陷阱
  • 陷阱1:shared_ptr如果出现相互引用的情况,就会导致计数永远不会减少为0,从而导致内存泄漏。

示例如下:

#include <memory>

struct B; // 前向声明

struct A {
    std::shared_ptr<B> b_ptr;
};

struct B {
    std::shared_ptr<A> a_ptr; 
};

int main() {
    auto a = std::make_shared<A>();
    auto b = std::make_shared<B>();
    a->b_ptr = b;
    b->a_ptr = a;

    return 0;
}

使用内存泄漏分析工具valgrind

g++ -g sharedptr.cpp -o sharedptr
valgrind --leak-check=full ./sharedptr

发现内存泄漏:

==588256== LEAK SUMMARY:
==588256==    definitely lost: 32 bytes in 1 blocks
==588256==    indirectly lost: 32 bytes in 1 blocks
==588256==      possibly lost: 0 bytes in 0 blocks
==588256==    still reachable: 0 bytes in 0 blocks
==588256==         suppressed: 0 bytes in 0 blocks

我们可以使用weak_ptr来处理这种情况。weak_ptr只指向shared_ptr管理的对象,但不会增加其引用计数,当最后一个shared_ptr被销毁时,weak_ptr所指向的对象就会被销毁,即使weak_ptr还指向它。

// ...

struct B {
    // 修改
    std::weak_ptr<A> a_ptr; 
};

// ...

内存泄漏问题解决:

==589233== All heap blocks were freed -- no leaks are possible
  • 陷阱2:unique_ptrshared_ptr在其析构函数中使用的使用delete而不是delete[]

这就意味着用它们管理动态分配的数组并不合适。

对于一些简单的数据类型,如intchar等,并不会造成内存泄漏,因为对它们而言deletedelete[]都能接受。

但是对于一些复杂的数据结构,如string,就会出现内存泄漏的情况。

#include<memory>
#include<string>

int main() {
    std::unique_ptr<std::string> aps(new std::string[10]);
}

使用内存泄漏分析工具valgrind

g++ -g ptrdelete.cpp -o ptrdelete
valgrind --leak-check=full ./ptrdelete

发现出现内存泄漏:

==552990== LEAK SUMMARY:
==552990==    definitely lost: 328 bytes in 1 blocks
==552990==    indirectly lost: 0 bytes in 0 blocks
==552990==      possibly lost: 0 bytes in 0 blocks
==552990==    still reachable: 0 bytes in 0 blocks
==552990==         suppressed: 0 bytes in 0 blocks

自定义类型Object

#include<iostream>
#include<memory>
#include<string>

using namespace std;

class Object {
public:
    Object(int len) 
    : _len(len) {
        // 使用new[]分配内存
        c_buffer = new char[_len];
    }
    ~Object() {
        // 使用delete[]释放内存
        delete[] c_buffer;
    }
private:
    // 字符数组
    char * c_buffer;
    int _len;
};

int main() {
    // 使用unique_ptr管理Object数组对象。
    std::unique_ptr<Object> spi(new Object[5]{5, 5, 5, 5, 5});
}

同样会出现内存泄漏:

==556994== LEAK SUMMARY:
==556994==    definitely lost: 88 bytes in 1 blocks
==556994==    indirectly lost: 20 bytes in 4 blocks
==556994==      possibly lost: 0 bytes in 0 blocks
==556994==    still reachable: 0 bytes in 0 blocks
==556994==         suppressed: 0 bytes in 0 blocks

所以我们应该使用vector这类容器来取代动态分配的数组,而非使用智能指针来管理动态分配的数组。

  • 43
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值