C++-标准库 智能指针smart pointer

概述

指针让我们在域边界之外拥有reference语义,然而,确保“pointer的寿命”和“其所指对象的寿命”一致却是件棘手的事。
C++11提供两大类型智能指针:shared_ptr和unique_ptr。

  • shared_ptr实现共享式拥有的概念。多个shared_ptr指向同一个对象,当最后一个shared_ptr析构时,指向的对象也被释放,标准库还提供weak_ptr, bad_weak_ptr, enable_shared_from_this等辅助类。
  • unique_ptr实现独占式拥有的概念。保证同一时间内只有一个指针指向对象,你可以移交所有权。

shared_ptr使用

shared_ptr的目标就是在其所指的对象不再被需要后,自动释放与之相关的资源。
构造:

shared_ptr<string> pStr1(new string("abc"));
shared_ptr<string> pStr2 = make_shared<string>("abc");

其中模板参数类型是指针指向的数据类型。
接受单一指针的构造函数是explicit的,因此不能进行隐式转换

shared_ptr<string> pStr = new string("abc");  // ERROR
shared_ptr<string> pStr{new string("abc")};  // OK

你也可以先声明shared_ptr再赋值一个指针给它。

shared_ptr<string> pStr;
pStr.reset(new string("abc"));
pStr = new string("abc"); // ERROR

你可以像使用指针一样使用shared_pointer

pStr->replace(0, 1, "J);
(*pStr)[0] = 'N';  // 改变string第一个元素

容器总是为传入的元素创建属于容器自己的拷贝,因此当把同一个shared_ptr重复添加到vector中时,多个shared_ptr将指向同一个string对象,当最后一个shared_ptr销毁时,会对其指向的对象调用delete
然而对于array,我们应该调用的是delete[],而不是delete,因此创建一个指向array的shared_ptr是错误的

shared_ptr<int> p(new int[10]); // ERROR

我们在第二个参数指定deleter

shared_ptr<int> p(new int[10], [](int*p) { delete[] p; });
shared_ptr<int> p(new int[10], std::default_delete<int[]>()); // default_delete默认调用delete[]

C++17之前,只有unique_ptr支持数组类型的模板

unique_ptr<int[]> p(new int[10]); // 正确。并且能够正确调用delete[]
shared_ptr<int[]> p(new int[10]); // 编译错误

C++17之后,shared_ptr也支持数组类型的模板,并且也可以像原生数组一样取数组元素

shared_ptr<int[]> p(new int[10]); // C++17以后正确
p[0] = 0; // C++17之后正确
p->get()[1] = 1; // C++11不支持operator[]可使用此写法,与上述语句等价

注:本人在visual studio上使用shared_ptr<int[]> p(new int[10]);没有检测到内存泄漏,可能是vs进行了优化?

shared_ptr误用

当我们为两组shared_ptr设定相同的指针,会造成double free或者资源被提前释放。如下:

int* p = new int;
shared_ptr<int> sp1(p);
shared_ptr<int> sp2(p);
// double free

另一个例子:

#include <memory>
#include <iostream>

using namespace std;

class Person
{
public:
    Person(const string& name) : m_name(name)
    {
    }
    ~Person()
    {
        cout << "destroy: " << m_name << endl;
    }
    void meetSomeone(shared_ptr<Person> someone)
    {
        m_otherOne = someone;
        someone->m_otherOne = shared_ptr<Person>(this);  // --> 2
    }

public:
    string m_name;
    std::weak_ptr<Person> m_otherOne;
};

int main()
{
    auto Alice = std::make_shared<Person>("Alice");  // --> 1
    auto Bob = std::make_shared<Person>("Bob");
    Alice->meetSomeone(Bob);
    return 0;
}

当我们执行程序会发生错误,这是因为Alice Person对象被1 2处两组shared_ptr管理,并且meetSomeone结束后,2处的临时shared_ptr会导致Alice Person对象被释放,程序将崩溃。关于weak_ptr的介绍详见这里,这里只要知道weak_ptr不影响shared_ptr的资源释放和use_count就行。

为了避免多组owner的问题,使用shared_ptr的原则是当我们为某个指针创建了shared_ptr后,之后应该都使用其相关的shared_ptr进行赋值和构造其他shared_ptr

int* p = new int;
shared_ptr<int> sp1(p);
shared_ptr<int> sp2(sp1);  // 使用sp1构造另一个shared_ptr

所谓“一组”shared_ptr,就是所有指针关联的shared_ptr的use_count都相同,假设有n个shared_ptr,那么use_count应都等于n**。对前一段代码而言,有2个shared_ptr,且两个shared_ptr的use_count()都为1,这就是“两组”shared_ptr。

使用class std::enable_shared_from_this

当我们需要在类内部的成员函数中使用当前对象关联的shared_ptr时,一种方法是通过参数传入shared_ptr

void meetSomeone(shared_ptr<Person> someone, shared_ptr<Person> this_shared_ptr)
{
    m_otherOne = someone;
    someone->m_otherOne = this_shared_ptr;  // --> 2
}
...
Alice->meetSomeone(Bob, Alice); // pass Alice shared_ptr

另一种简洁的方法就是让类继承class std::enable_shared_from_this,然后在成员函数中调用shared_from_this()。

class Person : public enable_shared_from_this<Person>
{
public:
    ...
    void meetSomeone(shared_ptr<Person> someone)
    {
        m_otherOne = someone;
        someone->m_otherOne = shared_from_this();
    }
    ...
};
...
Alice->meetSomeone(Bob);

参考:

  1. https://stackoverflow.com/questions/13061979/can-you-make-a-stdshared-ptr-manage-an-array-allocated-with-new-t
  2. https://blog.csdn.net/mrbone11/article/details/141873828?spm=1001.2014.3001.5501
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mrbone11

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值