C++智能指针

我们知道,new 在堆上分配内存,需要delete来删除内存,因为其不会自动的释放内存。然而智能指针(smart pointer)能够自动化的实现这一过程。
当调用new的时候,不再需要调用delete,甚至在很多使用智能指针的情况不需要使用new
智能指针在本质上是对原始指针的包装
当创建一个智能指针的时候,会调用new,并且分配内存,然后基于使用的智能指针的类型,到达条件时自动释放内存(调用delete)。

unique_ptr

unique_ptr是一个作用域指针scope pointer,当这个指针超出作用域时,会被销毁,调用delete。
必须是唯一的,不能被复制,如果有两个unique_ptr指针指向同一块内存,其中一个释放,指向相同内存的另一个unique_ptr会指向一个已经被释放的内存。存在的意义就在于,保证这个指针的唯一性,只能有这一个指针指向对应的内存地址。
std::unique_ptr 是一种独占型的智能指针,表示所管理的对象只能由一个指针拥有。无法通过复制来共享其管理权,因此它的所有权只能通过转移(move semantics)来传递。

#include <iostream>
#include <memory>

class MyClass {
public:
    MyClass() { std::cout << "MyClass Constructor" << std::endl; }
    ~MyClass() { std::cout << "MyClass Destructor" << std::endl; }
};

int main() {
    // 创建一个 unique_ptr 管理 MyClass 对象
    std::unique_ptr<MyClass> ptr1 = std::make_unique<MyClass>();
    
    // 转移所有权
    std::unique_ptr<MyClass> ptr2 = std::move(ptr1);
    
    //unique_prt不能被复制,拷贝构造函数和拷贝构造操作符被删除了
    //std::unique_ptr<MyClass> en = ptr1;
    
    // ptr1 现在为空,ptr2 拥有对象
    if (!ptr1) {
        std::cout << "ptr1 is null" << std::endl;
    }
    
    return 0;
}

这是最简单的智能指针,非常有用且开销低(甚至没有开销),它只是一个栈分配对象,当栈分配对象死亡(销毁)时,它将在你的指针上调用delete ,并释放内存。前面提到了unique_ptr 不能被复制。如果你去看它的定义,你会发现它的拷贝构造函数和拷贝构造操作符实际上被删除了,这就是为什么你运行如下代码时会编译错误。

note: copy constructor is implicitly deleted because 'unique_ptr<MyClass>' has a user-declared move constructor
  unique_ptr(unique_ptr&& __u) _NOEXCEPT

这是专门用来防止自掘坟墓的,因为你不能复制这个:其中一个指针被释放后,这个堆分配对象的底层内存会被释放。(意思就是说它其实不是指针而是个对象)所有指向这块堆内存的指针都将悬空。
所以如果你想“共享”这个指针,这就是shared pointer(共享指针)的用处所在了。
特性

  • 独占所有权:std::unique_ptr 不能复制,但可以通过 std::move 转移所有权。
  • 轻量级:由于它的独占性,std::unique_ptr 的开销比 std::shared_ptr 小。

shared pointer

std::shared_ptr 是一种共享所有权的智能指针,多个指针可以共同拥有一个对象。当最后一个 shared_ptr 被销毁时,所管理的对象才会被释放。它内部维护了一个引用计数(reference count)来记录有多少 shared_ptr 指向同一个对象。跟踪你的指针有多少个引用。一旦引用计数达到0,它就被删除了。

#include <iostream>
#include <memory>

class MyClass {
public:
    MyClass() { std::cout << "MyClass Constructor" << std::endl; }
    ~MyClass() { std::cout << "MyClass Destructor" << std::endl; }
};

int main() {
    // 创建一个 shared_ptr 管理 MyClass 对象
    std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>();
    
    {
        // ptr2 和 ptr1 共享对象的所有权
        std::shared_ptr<MyClass> ptr2 = ptr1;
        std::cout << "Reference count: " << ptr1.use_count() << std::endl;
    }// ptr2 离开作用域,引用计数减少
    
    std::cout << "Reference count: " << ptr1.use_count() << std::endl;
    
    return 0;
}

特性

  • 共享所有权:可以有多个 shared_ptr 指向同一个对象,只有当所有的 shared_ptr 被销毁时,对象才会被释放。
  • 引用计数:每个 shared_ptr 都会增加引用计数,保证安全的内存释放。
  • 适合复杂对象生命周期管理:例如多个对象可能共享一个资源时使用。

weak_ptr

std::weak_ptr 是一种不影响对象生命周期的智能指针。它配合 std::shared_ptr 使用,用于解决循环引用的问题。当 weak_ptr 指向的对象已经被销毁时,weak_ptr 不会访问它,也不会增加引用计数。
当仅仅是对指针对象进行操作,不参与对象的生命周期管理,不需要所有权时,可以std::shared_ptr赋值给std::weak_ptr

#include <iostream>
#include <memory>

class MyClass {
public:
    MyClass() { std::cout << "MyClass Constructor" << std::endl; }
    ~MyClass() { std::cout << "MyClass Destructor" << std::endl; }
};

int main() {
    std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>();
    std::weak_ptr<MyClass> weakPtr = ptr1;  // 不增加引用计数
    std::cout << ptr1.use_count()  << std::endl;//当前shared_ptr引用个数
    
    //一种常见的使用 std::weak_ptr 的方式,
    //它的目的是检查由 std::weak_ptr 所指向的对象是否仍然存在,并安全地获取其共享所有权。
    //使用 auto 关键字自动推导变量类型。
    //这里,auto 将推导出 sharedPtr 的类型为 std::shared_ptr<T>,T 是 weakPtr 所指向的对象类型。
    //lock() 返回的 std::shared_ptr 可以隐式转换为 bool。
    //如果 sharedPtr 不为空(即对象仍然存在),则 if 条件为真;如果对象已经被销毁,sharedPtr 为空,条件为假。
    if (auto sharedPtr = weakPtr.lock()) {
    	std::cout << ptr1.use_count()  << std::endl;//当前shared_ptr引用个数
        std::cout << "Object is still alive" << std::endl;
    } else {
        std::cout << "Object has been destroyed" << std::endl;
    }
    
    return 0;
}

weak_ptr使用std::weak_ptr 没有直接访问所指向对象的能力,因为它不增加引用计数。为了访问所指向的对象,必须通过 lock() 方法。这会返回一个 std::shared_ptr,如果对象还存在,lock() 会成功获取该对象的共享所有权。如果对象已经被销毁,lock() 返回一个空的std::shared_ptr

特性

  • 不参与引用计数:weak_ptr 不增加 shared_ptr 的引用计数,因此不会影响对象的生命周期。
  • 避免循环引用:当两个对象互相持有 shared_ptr 时,可能会导致循环引用问题,weak_ptr 可以打破这个循环。

使用场景总结

  • std::unique_ptr:适用于确保独占访问资源的场景,简单高效,自动释放内存。
  • std::shared_ptr:适用于资源需要在多个对象之间共享的场景,它提供自动化的内存管理。
  • std::weak_ptr:适用于防止循环引用或弱观察者模式,它不影响对象的生命周期。
    通过使用智能指针,C++ 程序员可以更方便地管理动态内存的分配和释放,从而减少内存泄漏的风险并编写更稳定的代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

阿波茨的鹅

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

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

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

打赏作者

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

抵扣说明:

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

余额充值