智能指针(shared_ptr,unique_ptr)作为函数参数或者返回值时的一些注意事项

本文探讨了在C++中使用智能指针(shared_ptr, unique_ptr)作为函数参数时的最佳实践,强调了如何正确地表达所有权转移及避免不必要的性能损失。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

智能指针(shared_ptr,unique_ptr)作为函数参数或者返回值时的一些注意事项

 

当智能指针作为函数的参数或者返回值时,一直在纠结到底是用智能指针对象本身还是用原始指针。Herb Sutter大师的文章很好的解决了这个疑惑,参见网址:

https://herbsutter.com/2013/06/05/gotw-91-solution-smart-pointer-parameters/

       总结起来如下

1、 不要传递shared_ptr本身,而是用原始指针。因为会有性能损失,原子操作的自增自减等。

使用f(widget *w)

不使用f(shared_ptr< widget > w)

函数的返回值也是同样的道理。

2当表示所有权的转移时,用unique_ptr作为函数参数。

 

Guideline: Don’t pass a smart pointer as a function parameter unless you want to use or manipulate the smart pointer itself, such as to share or transfer ownership.

 

Guideline: Prefer passing objects by value, *, or &, not by smart pointer.

 

Guideline: Express a “sink” function using a by-value unique_ptr parameter.

 

Guideline: Use a non-const unique_ptr& parameter only to modify the unique_ptr.

 

Guideline: Don’t use a const unique_ptr& as a parameter; use widget* instead.

 

Guideline: Express that a function will store and share ownership of a heap object using a by-value shared_ptr parameter.

 

Guideline: Use a non-const shared_ptr& parameter only to modify the shared_ptr. Use a const shared_ptr& as a parameter only if you’re not sure whether or not you’ll take a copy and share ownership; otherwise use widget* instead (or if not nullable, a widget&).

转载于:https://www.cnblogs.com/ljy339/p/11435265.html

<think>我们正在讨论C++中的四种智能指针shared_ptr, unique_ptr, weak_ptr, 以及auto_ptr(注意auto_ptr已被弃用)。我将根据用户提供的引用和我的知识来详细解释它们的用途和区别。 首先,注意auto_ptrC++11中已被弃用,在C++17中被移除,因此不推荐使用。我们将重点讨论其他三种。 1. **shared_ptr**: - 用途:共享所有权。多个shared_ptr可以指向同一个对象,通过引用计数来管理对象的生命周期。当最后一个shared_ptr被销毁,对象会被删除。 - 特点: - 引用计数是原子的,因此线程安全(但指向的对象本身不是线程安全的)。 - 避免使用裸指针初始化多个shared_ptr,否则会导致双重释放(如引用[2]和[4]所述)。 - 推荐使用`std::make_shared`来创建,因为只分配一次内存(同存储对象和控制块),效率更高(引用[4])。 - 不要用栈上的指针构造(如引用[1]所示),因为栈上的对象会在作用域结束自动销毁,而shared_ptr会试图delete它,导致未定义行为。 2. **unique_ptr**: - 用途:独占所有权。同一间只能有一个unique_ptr指向一个对象。当unique_ptr被销毁,它所指向的对象也会被销毁。 - 特点: - 轻量级,没有引用计数的开销。 - 不可复制,但可以移动(使用std::move)。 - 适用于资源独占的场景,比如在函数内部动态分配的对象,或者作为工厂函数的返回值。 - 可以自定义删除器(deleter),用于释放资源(比如文件句柄,使用fclose)。 3. **weak_ptr**: - 用途:解决shared_ptr的循环引用问题。weak_ptr指向一个由shared_ptr管理的对象,但不增加引用计数。因此,它不会影响对象的生命周期。 - 特点: - 不能直接访问对象,必须通过lock()方法转换成shared_ptr来使用(引用[3])。 - 用于观察对象,而不拥有对象。例如,在观察者模式中,观察者持有被观察者的weak_ptr,避免循环引用。 - 使用expired()或use_count()可以检查对象是否还存在。 4. **auto_ptr** (已弃用): - 用途:在C++98中引入,用于独占所有权,但在C++11中被unique_ptr取代。 - 特点: - 存在所有权转移的问题:当复制一个auto_ptr,原auto_ptr会失去所有权(变为nullptr),这容易导致意外。 - 因此,C++11中引入了unique_ptr,它通过禁止复制(允许移动)来更安全地实现独占所有权。 **使用场景总结**: - 需要共享所有权:使用`shared_ptr`(例如,多个对象需要共享同一个资源)。 - 需要独占所有权:使用`unique_ptr`(例如,在函数中动态分配对象,并在函数结束自动释放)。 - 需要打破循环引用:使用`weak_ptr`(例如,在双向链表、树结构或观察者模式中)。 - 避免使用`auto_ptr`。 **注意事项**: - 避免使用裸指针初始化智能指针,特别是同一个裸指针初始化多个智能指针(会导致多次释放)。 - 优先使用`make_shared`和`make_unique`(C++14引入)来创建智能指针,因为它们更安全(避免裸指针泄露)且更高效(减少内存分配次数)。 - 不要将栈上对象的地址给智能指针智能指针默认使用delete释放,而栈对象会自动销毁)。 下面用代码示例说明: ```cpp #include <memory> #include <iostream> class MyClass { public: MyClass() { std::cout << "MyClass constructed\n"; } ~MyClass() { std::cout << "MyClass destroyed\n"; } }; void sharedPtrExample() { std::shared_ptr<MyClass> sp1 = std::make_shared<MyClass>(); // 引用计数=1 { std::shared_ptr<MyClass> sp2 = sp1; // 引用计数=2 } // sp2销毁,引用计数=1 // sp1销毁,引用计数=0,对象销毁 } void uniquePtrExample() { std::unique_ptr<MyClass> up(new MyClass()); // 独占所有权 // std::unique_ptr<MyClass> up2 = up; // 错误,不能复制 std::unique_ptr<MyClass> up2 = std::move(up); // 可以移动,现在up为空 // 当up2离开作用域,对象销毁 } void weakPtrExample() { std::shared_ptr<MyClass> sp = std::make_shared<MyClass>(); std::weak_ptr<MyClass> wp = sp; // 不增加引用计数 if (auto locked = wp.lock()) { // 尝试提升为shared_ptr // 对象还存在,可以使用 } else { // 对象已被释放 } } int main() { sharedPtrExample(); uniquePtrExample(); weakPtrExample(); return 0; } ``` **输出**(大致顺序): ``` MyClass constructed MyClass destroyed // sharedPtrExample中sp1销毁 MyClass constructed // uniquePtrExample中创建 MyClass destroyed // uniquePtrExample中up2销毁 MyClass constructed // weakPtrExample中创建 MyClass destroyed // weakPtrExample中sp销毁 ``` **引用说明**: - 引用[1]和[2]提醒我们不要用栈上的指针初始化shared_ptr,以及避免混合使用原始指针和shared_ptr。 - 引用[3]解释了weak_ptr的作用和用法。 - 引用[4]说明了shared_ptr的创建方式和线程安全性。 最后,关于auto_ptr,由于已被弃用,我们只需知道它被unique_ptr取代即可。 §§相关问题§§ 1. 如何避免shared_ptr的循环引用问题? 2. make_shared和直接使用new创建shared_ptr有什么区别? 3. unique_ptr如何作为函数返回值传递? 4. weak_ptr的lock()方法在什么情况下返回空的shared_ptr? 5. 智能指针在多线程环境下的安全性如何?</think>### C++ 智能指针详解:shared_ptr, unique_ptr, weak_ptr, auto_ptr #### 1. **shared_ptr(共享指针)** - **核心机制**:基于引用计数的共享所有权模型 - **特点**: - 多个指针可共享同一对象 - 引用计数为0自动释放资源 - 线程安全的引用计数操作(原子操作)[^4] - **创建方式**: ```cpp // 推荐方式(单次内存分配) auto sp1 = std::make_shared<int>(10); // 传统方式(两次内存分配) std::shared_ptr<int> sp2(new int(20)); ``` - **使用场景**: - 需要多个对象共享同一资源 - 需要传递对象所有权但不确定何释放 - 避免使用裸指针初始化多个shared_ptr(防止双重释放)[^2] - **注意事项**: - 禁止用栈指针初始化(会导致双重释放)[^1] ```cpp int x = 12; // std::shared_ptr<int> ptr(&x); // 严重错误! ``` #### 2. **unique_ptr(独占指针)** - **核心机制**:独占所有权模型(不可复制) - **特点**: - 零运行开销(无引用计数) - 支持移动语义(`std::move`) - 可定制删除器(如文件句柄、自定义释放) - **创建方式**: ```cpp auto up = std::make_unique<std::string>("Hello"); ``` - **使用场景**: - 独占资源所有权的场景(如工厂模式返回值) - 替代已弃用的auto_ptr - 需要明确资源所有权的转移 - **所有权转移**: ```cpp auto src = std::make_unique<int>(42); auto dest = std::move(src); // src变为nullptr ``` #### 3. **weak_ptr(弱引用指针)** - **核心机制**:观察者模式(不增加引用计数) - **特点**: - 不拥有资源所有权 - 解决shared_ptr循环引用问题 - 需通过`lock()`转为shared_ptr访问资源[^3] - **使用场景**: ```cpp auto shared = std::make_shared<Resource>(); std::weak_ptr<Resource> observer = shared; if (auto locked = observer.lock()) { // 检查资源有效性 locked->use(); // 安全使用资源 } ``` - **关键方法**: - `expired()`:检查资源是否已被释放 - `use_count()`:获取当前引用计数 #### 4. **auto_ptr(已弃用)** - **历史地位**:C++98引入的独占指针 - **致命缺陷**: - 复制操作导致所有权静默转移 ```cpp auto_ptr<int> ap1(new int(5)); auto_ptr<int> ap2 = ap1; // ap1变为nullptr! ``` - **现状**: - C++11起被标记为deprecated - C++17中完全移除 - 必须用unique_ptr替代 #### 对比总结 | 特性 | shared_ptr | unique_ptr | weak_ptr | auto_ptr(弃用) | |---------------------|------------------|------------------|------------------|-----------------| | **所有权** | 共享 | 独占 | 无 | 独占 | | **复制语义** | ✅(增加计数) | ❌(仅移动) | ✅(不增计数) | ❌(转移所有权) | | **线程安全** | 引用计数原子操作 | 非线程安全 | 依赖shared_ptr | 非线程安全 | | **循环引用处理** | 需配合weak_ptr | 不适用 | 解决方案 | 不适用 | | **性能开销** | 中(控制块) | 低(无额外开销) | 低 | 低 | | **推荐使用场景** | 共享资源 | 独占资源 | 打破循环引用 | 禁止使用 | #### 最佳实践指南 1. **默认选择unique_ptr** ```cpp auto resource = std::make_unique<DatabaseConnection>(); ``` 2. **共享资源用shared_ptr+make_shared** ```cpp auto config = std::make_shared<GlobalConfig>(); ``` 3. **存在循环引用添加weak_ptr** ```cpp class Node { std::shared_ptr<Node> parent; std::vector<std::weak_ptr<Node>> children; // 弱引用打破循环 }; ``` 4. **绝对避免auto_ptr** ```cpp // std::auto_ptr<int> obsolete; // 已移除,编译错误 ``` > 智能指针的核心价值:**自动化资源生命周期管理**,消除内存泄漏和悬空指针风险[^1][^3][^4]。现代C++开发中,应优先使用make_shared/make_uniqueC++14起)创建智能指针,既保证异常安全又提升性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值