C++中shared_ptr的使用

本文详细介绍了C++11标准库中的shared_ptr,包括其作用、所有权转移、引用计数处理、空指针与NULL指针的区别、常用函数以及使用实例。重点阐述了shared_ptr如何避免内存泄露,以及与普通指针的区别。

template class shared_ptr

#include<memory>

shared_ptr是C++ 11之后标准库引入的一个特性,用于简化内存管理,防止内存泄露。

1.shared_ptr的作用是什么?

Objects of shared_ptr types have the ability of taking ownership of a pointer and share that ownership: once they take ownership, the group of owners of a pointer become responsible for its deletion when the last one of them releases that ownership.注意,这个解释中有两个关键的词语take&&shared the ownership.

2.那么shared_ptr什么时候放弃对ownership的所有权呢?

shared_ptr objects release ownership on the object they co-own as soon as they themselves are destroyed, or as soon as their value changes either by an assignment operation or by an explicit call to shared_ptr::reset. Once allshared_ptr objects that share ownership over a pointer have released this ownership, the managed object is deleted (normally by calling ::delete, but a different deleter may be specified on construction).

3.引用计数的处理:两个shared_ptr指向同一个pointer时,情况如何?

shared_ptr objects can only share ownership by copying their value: If two shared_ptr are constructed (or made) from the same (non-shared_ptr) pointer, they will both be owning the pointer without sharing it, causing potential access problems when one of them releases it (deleting its managed object) and leaving the other pointing to an invalid location.

4.empty VS null pointer

shared_ptr that does not own any pointer is called an empty shared_ptr. A shared_ptr that points to no object is called a null shared_ptr and shall not be dereferenced. Notice though that an empty shared_ptr is not necessarily anull shared_ptr, and a null shared_ptr is not necessarily an empty shared_ptr.

5.常用函数

use_count
reset

6.使用实例

#include <iostream>
#include <memory>
using namespace std;
class A{
    public:
        int a;
        int b;
        int c;
        int d;
};//16B

std::shared_ptr<A> test(){
    for (int i = 0; i < 1; ++i)
    {
        std::shared_ptr<A> pa(new A);
        return pa;
    }
}
int main(int argc, char *argv[])
{
    for (int i = 0; i < 1000*1000; ++i)
    {
        std::shared_ptr<A> pa = test();
        cout << pa.use_count();
    }
    while(1);
    return 0;
}

上面是shared_ptr的使用实例,我们可以看到,其中并没有delete操作。我们将shared_ptr换成普通指针,然后利用top命令进行对比,就可以看到二者的所需要的内存大小的不同。

[1] weak_ptr
[2] shared_ptr包含在哪些库中?go to shared_ptr

<think> 我们被要求介绍C++shared_ptr的用法。根据引用内容,shared_ptrC++11中引入的智能指针,用于自动管理内存,避免内存泄漏。下面将详细介绍其用法。 主要知识点: 1. shared_ptr的基本概念:共享所有权,使用引用计数机制。 2. 初始化shared_ptr的多种方式。 3. shared_ptr的常用成员函数。 4. 使用shared_ptr的注意事项。 下面分点详细说明: ### 一、shared_ptr的基本概念 `shared_ptr`是一种智能指针,它记录着多个指针指向同一个对象的引用计数。当最后一个指向对象的`shared_ptr`被销毁时,对象会被自动删除。这避免了手动调用`delete`,从而减少了内存泄漏的风险。 ### 二、初始化shared_ptr #### 1. 使用构造函数 ```cpp std::shared_ptr<int> sp1(new int(10)); // 正确:直接初始化 ``` #### 2. 使用make_shared函数(推荐) ```cpp std::shared_ptr<int> sp2 = std::make_shared<int>(10); // 更高效 ``` 使用`std::make_shared`在动态分配内存的同时创建控制块(存储引用计数),通常效率更高,且能保证异常安全。 #### 3. 错误初始化方式 ```cpp std::shared_ptr<int> sp3 = new int(10); // 错误:构造函数是explicit的 ``` ### 三、shared_ptr的常用操作 #### 1. 获取原始指针 ```cpp int* raw_ptr = sp1.get(); // 获取原始指针,但不增加引用计数 ``` 注意:不要手动删除该指针,也不要用它创建另一个shared_ptr,否则可能导致重复释放。 #### 2. 重置指针 ```cpp sp1.reset(); // 释放所管理的对象,sp1变为空 sp1.reset(new int(20)); // 释放原对象,管理新对象 ``` #### 3. 查看引用计数 ```cpp long count = sp1.use_count(); // 返回引用计数(注意:在多线程环境下可能不准确) ``` ### 四、shared_ptr的拷贝与赋值 拷贝`shared_ptr`会增加引用计数: ```cpp std::shared_ptr<int> sp4 = sp1; // 引用计数加1 ``` ### 五、自定义删除器 当对象不是通过`new`创建,或者需要特殊删除操作时,可以指定自定义删除器: ```cpp void my_deleter(int* p) { delete p; } std::shared_ptr<int> sp5(new int(30), my_deleter); // 或者使用lambda表达式 std::shared_ptr<int> sp6(new int(40), [](int* p){ delete p; }); ``` ### 六、注意事项 1. **不要用同一个原始指针初始化多个shared_ptr**: ```cpp int* p = new int(50); std::shared_ptr<int> sp7(p); std::shared_ptr<int> sp8(p); // 错误:会导致重复释放 ``` 2. **避免循环引用**:如果两个对象互相持有对方的`shared_ptr`,会导致引用计数永远不为0,从而内存泄漏。解决方案:使用`weak_ptr`打破循环。 3. **避免使用get()返回的指针创建shared_ptr**:这会导致多个控制块,同样会有重复释放的风险。 4. **多线程安全性**:`shared_ptr`的引用计数本身是原子操作,线程安全。但其所指向的对象则不一定安全,需要额外保护。 ### 七、示例代码 ```cpp #include <memory> #include <iostream> class MyClass { public: MyClass() { std::cout << "MyClass constructed\n"; } ~MyClass() { std::cout << "MyClass destroyed\n"; } }; int main() { // 使用make_shared创建 std::shared_ptr<MyClass> sp1 = std::make_shared<MyClass>(); { // 拷贝构造,引用计数增加 std::shared_ptr<MyClass> sp2 = sp1; std::cout << "Reference count: " << sp1.use_count() << std::endl; // 输出2 } // sp2离开作用域,引用计数减1 std::cout << "Reference count: " << sp1.use_count() << std::endl; // 输出1 return 0; } // sp1离开作用域,引用计数为0,对象销毁 ``` 输出: ``` MyClass constructed Reference count: 2 Reference count: 1 MyClass destroyed ``` ### 八、总结 `shared_ptr`是C++中管理动态内存的重要工具,它通过引用计数实现自动内存管理。使用时应注意初始化方式(推荐`make_shared`),避免循环引用和重复管理同一原始指针。在多线程环境中,要确保对共享对象的访问安全[^1][^2][^3]。 §§相关问题§§ 1. 什么是C++中的weak_ptr?它如何解决循环引用问题? 2. shared_ptr和unique_ptr有什么区别?各自适用什么场景? 3. 如何用shared_ptr管理数组? 4. shared_ptr的线程安全性如何保证? 5. 自定义删除器在实际开发中有哪些应用场景? </think> ### C++shared_ptr用法详解 `shared_ptr`是C++11引入的智能指针,用于**自动管理动态内存**,通过**引用计数机制**实现多指针共享对象所有权。当最后一个`shared_ptr`销毁时,自动释放内存,避免内存泄漏[^1][^2][^3]。 --- #### 一、基本用法 ##### 1. 初始化方式 ```cpp // 原始指针构造(不推荐,有安全隐患) std::shared_ptr<int> sp1(new int(42)); // make_shared构造(推荐,更高效安全) auto sp2 = std::make_shared<std::string>("Hello"); // 拷贝构造(引用计数+1) auto sp3 = sp2; ``` ##### 2. 关键操作 ```cpp sp1.use_count(); // 获取引用计数:1 sp2.reset(); // 释放对象所有权(引用计数-1) sp1.get(); // 获取原始指针(谨慎使用) ``` --- #### 二、核心特性 1. **引用计数机制** ```mermaid graph LR A[shared_ptr1] --> C[对象X] B[shared_ptr2] --> C C --> D[引用计数=2] ``` 当最后一个`shared_ptr`离开作用域时,对象自动销毁。 2. **线程安全** 引用计数操作是原子的,但指向的对象访问需要额外同步[^3]。 3. **自定义删除器** ```cpp // 文件指针的自定义删除 auto file_deleter = [](FILE* f) { if(f) fclose(f); }; std::shared_ptr<FILE> fp(fopen("data.txt", "r"), file_deleter); ``` --- #### 三、使用场景 1. **共享对象所有权** ```cpp class NetworkConnection { public: void send(const std::string& data); }; auto conn = std::make_shared<NetworkConnection>(); auto worker1 = [conn] { conn->send("data1"); }; auto worker2 = [conn] { conn->send("data2"); }; // 两个线程共享同一个连接对象 ``` 2. **容器存储指针** ```cpp std::vector<std::shared_ptr<Shape>> shapes; shapes.push_back(std::make_shared<Circle>()); shapes.push_back(std::make_shared<Rectangle>()); ``` --- #### 四、注意事项 1. **禁止混用原始指针** ```cpp int* raw = new int(100); std::shared_ptr<int> p1(raw); std::shared_ptr<int> p2(raw); // 错误!会导致双重释放 ``` 2. **避免循环引用** 使用`weak_ptr`打破循环: ```cpp struct Node { std::shared_ptr<Node> next; std::weak_ptr<Node> prev; // 弱引用避免循环 }; ``` 3. **性能考虑** - `make_shared`比`new`高效(单次内存分配) - 引用计数带来约10-20%性能开销[^1] --- #### 五、与unique_ptr对比 | 特性 | shared_ptr | unique_ptr | |------|------------|------------| | 所有权 | 共享 | 独占 | | 拷贝语义 | ✅ 允许 | ❌ 禁止 | | 移动语义 | ✅ | ✅ | | 自定义删除器 | ✅ | ✅ | | 内存开销 | 控制块(约16-32字节) | 无额外开销 | > 优先使用`unique_ptr`,仅在需要共享所有权时用`shared_ptr`[^3]。 --- ### 总结 `shared_ptr`是C++内存管理的核心工具,通过: 1. 使用`make_shared`初始化 ✅ 2. 避免原始指针混用 ✅ 3. 用`weak_ptr`解决循环引用 ✅ 4. 了解性能开销 ✅ 可显著提升代码安全性和可维护性[^1][^2][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值