云计算百科
云计算领域专业知识百科平台

C++ 智能指针 shared_ptr /unique_ptr/weak_ptr

一 unique_ptr独占智能指针

独占所有权,同一时间只能有一个unique_ptr指向这块堆内存,禁止拷贝,只允许移动。

  不能拷贝:unqiue_ptr<int> p2 = p1;编译报错;

  可以移动:unique_ptr<int> p2 = move(p1);移动后p1变成空,p2接管资源;

  生命周期:出作用域,自动调用delete释放内存;

示例代码:

#include <iostream>
#include <memory>
using namespace std;

int main()
{
// 创建unique_ptr,管理堆上int
unique_ptr<int> p1(new int(100));
cout << *p1 << endl;

// unique_ptr<int> p2 = p1; // ❌ 编译报错!禁止拷贝,独占

unique_ptr<int> p2 = move(p1); // ✅ 移动所有权
cout << *p2 << endl;
// p1 现在是空指针,不能再使用*p1

return 0;
} // 出作用域,p2销毁,自动释放堆内存,不用手动delete

要点:

1.底层:析构的时候直接释放内存,没有引用计数,开销很小;

2.适用场景:资源只需要一个管理者(绝大多数场景优先用unique_ptr);

3.C++14推荐写法:auto p = make_unique<int>(100);

二 shared_ptr共享智能指针

共享所有权,维护引用计数;每多一个shared_ptr指向同一块内存,计数+1;对象析构计数-1;引用计数减到0,释放堆内存。

  引用计数:单独存放在堆上,不是存在对象内部;

  拷贝允许:拷贝时引用计数+1;

  移动:原指针置空,计数不变;

示例代码:

#include <iostream>
#include <memory>
using namespace std;

int main()
{
shared_ptr<int> p1 = make_shared<int>(200); // 计数=1
cout << p1.use_count() << endl; // use_count查看引用计数

shared_ptr<int> p2 = p1; // ✅ 允许拷贝,共享资源,计数=2
cout << p2.use_count() << endl;

p1.reset(); // p1放弃管理,计数=1
cout << p2.use_count() << endl;

return 0;
} // 函数结束p2析构,计数变成0,释放内存

致命问题:循环引用(内存泄漏)

两个对象A,B,A里面存shared_ptr<B>,B里面存shared_ptr A持有B,B持有A。两个对象析构的时候,引用计数都减到1,永远不为0,内存永远不释放。

struct B;
struct A{
shared_ptr<B> b_ptr;
~A(){cout << "A析构"<<endl;}
};
struct B{
shared_ptr<A> a_ptr;
~B(){cout << "B析构"<<endl;}
};

int main()
{
shared_ptr<A> a = make_shared<A>();
shared_ptr<B> b = make_shared<B>();
a->b_ptr = b;
b->a_ptr = a;
// 出main,a、b析构,但是内部互相持有,引用计数=1
// A、B的析构函数**不会执行,内存泄漏!**
return 0;
}

运行结果:不会打印A析构,B析构,内存泄漏;

解决方案:把其中一个shared_ptr改为weak_ptr;

三 weak_ptr弱引用

不增加shared_ptr的引用计数,不拥有资源,只是“旁观”shared_ptr管理的对象

  weak_ptr不能直接解引用访问对象,必须调用.lock(),返回一个临时shared_ptr;

    lock()底层是如何操作的

  • 拿到 weak_ptr 保存的控制块地址(weak_ptr 内部只存控制块指针 + 对象指针,不持有对象)

  • 原子操作读取 use_count,判断 use_count > 0

    • 如果 use_count == 0:对象已经被释放 → 直接返回空的shared_ptr

    • 如果 use_count > 0:尝试原子地把 use_count +=1(原子!防止多线程并发释放)

  • 构造一个新的 shared_ptr,把对象指针、控制块指针交给这个新 shared_ptr 返回

  • 整个过程是原子的,防止多线程下 “判断完还没加计数,对象就被别的线程释放”

  •  可以用.expired()判断资源是否已经被释放;

      专门解决shared_ptr循环引用问题;

    修复上面循环引用代码(把A内部的shared_ptr<B>改为weak_ptr<B>)

    #include <iostream>
    #include <memory>
    using namespace std;

    struct B;
    struct A{
    weak_ptr<B> b_ptr; // 弱指针!不增加引用计数
    ~A(){cout << "A析构"<<endl;}
    };
    struct B{
    shared_ptr<A> a_ptr;
    ~B(){cout << "B析构"<<endl;}
    };

    int main()
    {
    shared_ptr<A> a = make_shared<A>();
    shared_ptr<B> b = make_shared<B>();
    a->b_ptr = b; // weak_ptr赋值,b的引用计数不会+1
    b->a_ptr = a;

    return 0;
    }
    // 输出:A析构 B析构,内存正常释放!

    当main函数结束:

    1.b析构,B内部a_ptr计数减1,a的引用计数变成0->A对象释放;

    2.A释放后,weak_ptr自动失效,没有指针再持有B,B的引用计数变成0->B释放。

    使用weak_ptr访问对象示例:

    shared_ptr<int> s = make_shared<int>(666);
    weak_ptr<int> w = s;
    // cout << *w; // ❌ weak_ptr不能直接解引用
    shared_ptr<int> tmp = w.lock(); // 拿到临时shared_ptr
    if(tmp != nullptr)
    {
    cout << *tmp << endl;
    }

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » C++ 智能指针 shared_ptr /unique_ptr/weak_ptr
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!