C++ 智能指针中的 std::shared_ptr 全面讲解

std::shared_ptr 是 C++11 引入的一种智能指针,它提供了自动化的内存管理功能,以防止内存泄漏。它通过引用计数来管理动态分配的对象,当最后一个 std::shared_ptr 被销毁时,该对象的内存会自动释放。

下面我将分别解释 std::shared_ptr 的优点、缺点、特性和建议搭配的使用场景,并附上相关的代码示例。

1. 优点
  • 自动内存管理:通过引用计数确保指针安全,避免内存泄漏。
  • 强所有权共享:多个 shared_ptr 可以共享同一个对象,当最后一个 shared_ptr 被销毁时,资源才会被释放。
  • 线程安全:引用计数的修改是原子的,适合多线程环境。
代码示例:
#include <iostream>
#include <memory>

void demonstrate_shared_ptr() {
    std::shared_ptr<int> sp1(new int(10));  // 创建一个 shared_ptr
    std::shared_ptr<int> sp2 = sp1;         // 复制 shared_ptr,引用计数+1
    std::cout << "sp1 use count: " << sp1.use_count() << std::endl;  // 输出:2

    sp2.reset();                             // 重置 sp2,引用计数-1
    std::cout << "sp1 use count: " << sp1.use_count() << std::endl;  // 输出:1
    std::cout << "sp2 is null: " << std::boolalpha << (sp2 == nullptr) << std::endl;  // 输出:true
}

int main() {
    demonstrate_shared_ptr();
    return 0;
}
2. 缺点
  • 性能开销:引用计数的更新涉及到原子操作,带来一些性能开销,特别是在多线程环境中。
  • 资源循环引用:如果两个或多个 shared_ptr 对象循环引用对方,会导致内存泄漏,因为引用计数永远不会归零。
  • 额外内存开销:管理引用计数需要额外的内存。
代码示例:循环引用(避免方法见最后的场景)
#include <iostream>
#include <memory>

struct Node {
    std::shared_ptr<Node> next;
    ~Node() {
        std::cout << "Node destroyed" << std::endl;
    }
};

void demonstrate_circular_reference() {
    std::shared_ptr<Node> node1 = std::make_shared<Node>();
    std::shared_ptr<Node> node2 = std::make_shared<Node>();

    node1->next = node2;
    node2->next = node1;

    // 这个例子中,Node 的析构函数不会被调用,导致内存泄漏
}

int main() {
    demonstrate_circular_reference();
    return 0;
}
3. 特性
  • 引用计数:多个 shared_ptr 可以共享同一对象,引用计数会自动维护,引用计数为零时对象被删除。
  • 线程安全:引用计数的增加和减少是线程安全的。
  • 自定义删除器:可以指定自定义资源释放逻辑。
代码示例:
#include <iostream>
#include <memory>

struct Resource {
    ~Resource() {
        std::cout << "Resource destroyed" << std::endl;
    }
};

void custom_deleter(Resource* res) {
    std::cout << "Custom delete for Resource" << std::endl;
    delete res;
}

void demonstrate_custom_deleter() {
    std::shared_ptr<Resource> sp(new Resource(), custom_deleter);
    // sp goes out of scope here, invoking custom_deleter
}

int main() {
    demonstrate_custom_deleter();
    return 0;
}
4. 建议搭配使用场景
  • 适合需要共享所有权的对象:如缓存、资源管理器等。
  • 避免循环引用(搭配 std::weak_ptr 使用)
代码示例:利用 std::weak_ptr 避免循环引用
#include <iostream>
#include <memory>

struct Node {
    std::weak_ptr<Node> next;  // 使用 weak_ptr 避免循环引用
    ~Node() {
        std::cout << "Node destroyed" << std::endl;
    }
};

void demonstrate_weak_ptr() {
    std::shared_ptr<Node> node1 = std::make_shared<Node>();
    std::shared_ptr<Node> node2 = std::make_shared<Node>();

    node1->next = node2; 
    node2->next = node1;  // weak_ptr 不增加引用计数

    // Node 的析构函数将被调用,因为没有循环引用
}

int main() {
    demonstrate_weak_ptr();
    return 0;
}

总结

  • 优点shared_ptr 提供自动内存管理和线程安全的强所有权共享。
  • 缺点:存在性能开销,可能导致循环引用问题,需注意防范。
  • 特性:包括引用计数、线程安全和支持自定义删除器。
  • 建议使用场景:适合需要共享所有权的场景,并且应在可能的循环引用情况下使用 std::weak_ptr
  • 19
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值