智能指针

C++11中的智能指针主要分为3种:std::unique_ptrstd::shared_ptrstd::weak_ptr,存在于头文件 <memory>中。

对于智能指针的创建一般优先建议,分别使用 make_uniquemake_shared,而不是使用new的指针来构建。这种方式创建智能指针有如下几个优点:

  1. 效率高,避免多次的内存分配。智能指针一般包括:指向资源的指针和引用计数的控制块,使用make的方式可以一次分配对于的内存,避免 make 一次、new 又分配一次。
  2. 提高了异常安全性,避免出现异常时可能导致的内存泄漏。

一、专有指针:std::unique_ptr

std::unique_ptr用于独占资源,不允许存在其它的指针也指向相同的资源。因此std::unique_ptr将不能进行拷贝和赋值,只能进行移动操作。移动std::unique_ptr后源指针将为null,所有的资源将会转移到目的指针上。

1、声明构建专有指针的方法如下:

    // 声明空指针
    unique_ptr<string> up1;
    unique_ptr<string> up2(nullptr);

    // 推荐写法直接使用 make_unique 来创建智能指针
    unique_ptr<string> up0 = make_unique<string>("test");

    // 会代码额外开销,进行了两次内存分配操作
    unique_ptr<string> up3(new string("test"));

    // 不推荐写法
    string* pStr = new string("test");
    unique_ptr<string> up4(pStr);

    // 专有指针不能赋值,只能转移给另一个专有指针
    up1 = std::move(up0);
    //up2 = up3; // 错误,无法进行赋值操作

2、专有指针作为函数的形参,只能为引用的形式

由于专有指针独占资源,因此其作为形参时如果以值传递的形式出现,将会导致拷贝了另外的指针来指向相同的资源,因此只能以引用的形式存在。如下所示:

// uPtr为专有指针,只能以引用的形式存在
void tmp_fun1(unique_ptr<SmartPoint> &uPtr)
{
    string city_u = uPtr->GetCity();
}

二、共享指针:std::shared_ptr

对于共享资源将使用std::shared_ptr,由多个 std::shared_ptr实例来共同管理对应的资源,通过引用计数来控制何时将释放该资源,当引用计数为0时,该资源将会被释放。
std::shared_ptr内部包含两个指针:

  • 指向内存对象的指针:相当于原始指针,指向分配的内存对象
  • 指向控制块的指针:该指针相对于原始指针,为多出的部分,其指向控制块。控制块中包含引用计数、弱引用计数,以及其它内容。所有std::shared_ptr将指向同一个对象,并共用控制块。

1、该指针的构建方式如下所示:

    /****** SmartPointer 为自定义类 ******/

    // 声明指针
    shared_ptr<SmartPointer> sPtr1;
    shared_ptr<SmartPointer> sPtr2(nullptr);

    // 推荐定义智能指针的方式
    shared_ptr<SmartPointer> sPtr3 = make_shared<SmartPointer>(6, "sz02", 88.66);

    // 该方式创建指针性能差于make的方式
    shared_ptr<SmartPointer> sPtr4(new SmartPointer(6, "sz03", 88.66));

    // 不推荐该方法:使用原始指针来构建智能指针
    SmartPointer* p1 = new SmartPointer(1, "city01", 10.66);
    shared_ptr<SmartPointer> sPtr5(p1);

2、避免给std::shared_ptr的构造函数传递原始指针

SmartPointer* p1 = new SmartPointer(1, "city01", 10.66);
shared_ptr<SmartPointer> sPtr1(p1);
shared_ptr<SmartPointer> sPtr2(p1);

以上代码中使用原始指针 p1来作为参数来分别构建智能指针 sPtr1sPtr2,此时sPtr1sPtr2将分别创建对应的控制块,而且各自的控制块中引用计数将为1。当sPtr1的计数为0时,将释放其指向的内存资源。此时sPtr2的计数在为0时,又将释放一次指向的资源,但是该资源已经被前面释放了,因此会导致 doble free的情况。
对于必须使用 new 的原始指针来构建智能指针的情况,建议直接在智能指针的构造函数中使用 new 。

3、引用计数

std::shared_ptr的引用计数是根据当前有多少指针指向对应的资源来计算的

// 构建两个智能指针,其指向整型数值10
shared_ptr<int> sp1(make_shared<int>(10));  // sp1 指向的控制块中引用计数为1
auto sp2(make_shared<int>(10));             // sp2 指向的控制块中引用计数为1

// 创建指针sp3, 让其也指向sp1的资源
shared_ptr<int> sp3(sp1);     // sp1 指向的控制块中引用计数将加1,变成2;sp1和sp3共用一个控制块


三、弱指针:std::weak_ptr

std::shared_ptr可能悬空时使用std::weak_ptr,此时指针所指的对象可能已经被释放了。
std::weak_ptr一般需要使用std::shared_ptr来进行初始化,它不是一个独立的智能指针,也不能解引用,它是std::shared_ptr的增强。它与std::shared_ptr所指的对象相同,但是std::weak_ptr不会增加std::shared_ptr的引用计数。

auto sp1(make_shared<int>(66));
weak_ptr<int> wPtr1(sp1);

【参考资料】
创建和使用 shared_ptr 实例
对于共享资源使用std::shared_ptr

posted @   Jeffxue  阅读(165)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示