使用auto_ptr需要注意的事项

本文详细介绍了C++智能指针auto_ptr的基本概念、使用方式及其特性的关键点,包括所有权转移、复制与赋值行为、不能作为容器元素、析构行为、判断绑定状态的方法、构造函数的隐式转换限制、多auto_ptr绑定同一对象的后果、指向静态资源的限制以及主要操作方法等。

原文:http://patmusing.blog.163.com/blog/static/13583496020101824541270/

a. auto_ptr定义于头文件memory中;

 

b. auto_ptr只能用来管理单个动态创建的对象,而不能管理动态创建的数组;

 

c. 和其他copyassign不同,auto_ptrcopyassign会改变右边的操作数,assignment符号的两边的auto_ptr均为左值;There is a crucially important difference between how auto_ptr and built-in pointers treat copy and assignment. When we

copy an auto_ptr or assign its value to another auto_ptr, ownership of the underlying object is transferred from the original

to the copy. The original auto_ptr is reset to an unbound state

 

d. auto_ptr不能作为容器中的元素;

  auto_ptrcopyassign具有析构行为,这就是auto_ptr不能作为容器元素的原因,因为标准库中的容器有对元素的要求:经

copy或者assign后的两个对象,必须相等;

 

e. 在判断一个auto_ptr是否被绑定的时候,不能直接使用auto_ptr对象:

      auto_ptr<Student> stu1(new Student);

         if(stu1)

         {

                   cout << "stu1 is bound" << endl;

         }

         else

         {

                   cout << "stu1 is unbound" << endl;

         }

         这样做将会导致compile error,应该改为:

         auto_ptr<Student> stu1(new Student);

         if(stu1.get())               // get()获取的是underlying对象的指针,如果被绑定则非零,如果没有被绑定则为0

         {

                   cout << "stu1 is bound" << endl;

         }

         else

         {

                   cout << "stu1 is unbound" << endl;

         }

 

f. auto_ptr的构造函数是explicit的,消除了隐式的类型转换(在这里即,从指针类型到auto_ptr类型的转换),因此不能直接将一个

指针赋给一个auto_ptr对象。如下面这样的代码:

         auto_ptr<Student> stu5 = new Student;

         stu5->printStudentInfo();

  在编译的时候不会有问题,但会出现严重的runtime error。正确的做法应该是:

         auto_ptr<Student> stu5(new Student);

         stu5->printStudentInfo();

 

g. 不同用两个auto_ptr绑定到同一个对象。

      // stu6stu7绑定到了同一个对象,这将会导致该对象被析构两次,将会产生runtime error

         auto_ptr<Student> stu6(new Student("Evanligine", "F", 8));

         auto_ptr<Student> stu7(stu6.get());

         后面一句,应该改为:

         auto_ptr<Student> stu7(stu6);

         这样stu6就将ownership转交给了stu7stu6则成为了unboundauto_ptr            

 

h. 不能用auto_ptr指向静态资源分配对象。如下面的代码,尽管可以通过编译,但将会产生runtime error

         int ix = 10;

         auto_ptr<int> pint1(&ix);

 

i. auto_ptr的重要操作

auto_ptr<T> ap;               创建一个未绑定的auto_ptr对象ap

auto_ptr<T> ap(p);           创建一个auto_ptr对象ap,它绑定了指针p所指向的对象。该构造函数是explicit

auto_ptr<T> ap1(ap2);      创建一个auto_ptr对象ap1,它绑定到原来被ap2绑定的对象,ap2则成为未绑定的auto_ptr

ap1 = ap2;                       ap1删除原来绑定的对象,ap2ownership移交给ap1ap2成为未绑定的auto_ptr

*ap                                  返回ap绑定的对象的引用。可以通过*给被绑定的内在对象赋值。如下面代码:

                                                        auto_ptr<int> pint(new int(3));

                                                        cout << *pint << endl;                           // 输出3

                                                        *pint = 100;

                                                        cout << *pint << endl;                           // 输出100

ap->                                返回被ap绑定的对象的指针

ap.reset(p)                       如果指针pap绑定的内存对象的指针不相同,那么ap删除被其绑定的内存对象,改而绑定p

         指向的对象.

ap.release()                     返回ap所绑定对象的指针,并删除该被绑定的对象

ap.get()                           返回 ap 所绑定对象的指针
<think>好的,我现在需要回答用户的问题:“使用unique_ptr所有注意事项”。首先,我需要回顾之前的对话历史,看看用户之前询问了哪些内容,以及我之前的回答。用户之前问过关于unique_ptr使用方法示例,以及如何为int分配内存,所以现在的问题更深入,涉及注意事项。 首先,我得确定用户的需求。他们可能已经了解了unique_ptr的基本用法,但想确保在实际使用中不会犯错,尤其是在复杂场景下。因此,我需要详细列出使用unique_ptr时需要注意的关键点,涵盖所有权管理、资源释放、自定义删除器、与容器的配合、多态类型处理、线程安全等方面。 接下来,我需要组织内容的结构。可能分为核心规则、常见陷阱、高级注意事项、最佳实践等部分。同时,要注意给出代码示例,说明正确和错误的用法,帮助用户更直观地理解。 然后,检查是否有遗漏的重要点。例如,unique_ptr不能拷贝只能移动,这点非常重要,必须提到。还有数组特化时的delete[]使用,自定义删除器的兼容性,以及如何与STL容器结合使用时的移动语义。 另外,异常安全也是一个关键点,因为unique_ptr在异常抛出时能自动释放资源,避免内存泄漏。线程安全方面,虽然unique_ptr本身不是线程安全的,但在多线程中传递所有权是可行的,需要说明注意事项。 还需要考虑用户可能遇到的错误,比如错误地使用get()导致悬空指针,或者忘记检查unique_ptr是否为空。此外,移动后原指针变为null,访问会导致未定义行为,这也是常见错误点。 最后,确保回答符合用户要求的格式,包括代码块、加粗标题、注意事项分点列出,并在最后给出相关问题。要避免使用Markdown,但用户允许在示例中使用代码块,所以需要正确格式化代码。 现在,我需要将这些内容整理成一个结构清晰、涵盖全面的回答,确保每个注意事项都有足够的解释和示例支持,同时语言简洁明了。还要检查是否覆盖了用户可能关心的所有方面,比如所有权转移、资源泄漏预防、正确使用make_unique等。</think>### 使用 `unique_ptr` 的所有注意事项详解 --- #### 一、核心规则 1. **所有权唯一性** - **禁止拷贝**:`unique_ptr` 不能通过拷贝构造或赋值传递所有权 ```cpp std::unique_ptr<int> p1 = std::make_unique<int>(10); std::unique_ptr<int> p2 = p1; // ❌ 编译错误:拷贝构造被删除 ``` - **移动语义转移所有权**: ```cpp std::unique_ptr<int> p2 = std::move(p1); // ✅ 合法,p1 变为 null ``` 2. **资源释放时机** - **自动释放**:离开作用域时自动调用析构函数释放资源 ```cpp { auto ptr = std::make_unique<int>(42); } // 资源在此处自动释放 ``` - **手动释放**:使用 `reset()` 或 `release()` ```cpp ptr.reset(); // 立即释放资源(若存在) int* raw = ptr.release(); // 放弃所有权,需手动 delete ``` --- #### 二、常见陷阱与解决方案 1. **错误使用 `get()`** - **问题**:直接使用 `get()` 获取的原始指针可能引发悬空指针 ```cpp int* raw = ptr.get(); ptr.reset(); // 释放资源 *raw = 10; // ❌ 未定义行为:访问已释放内存 ``` - **解决方案**:仅在必要时使用 `get()`,并确保指针生命周期安全。 2. **数组特化误用** - **错误**:用单对象 `unique_ptr` 管理数组 ```cpp std::unique_ptr<int> arr(new int[10]); // ❌ 错误:析构时调用 delete 而非 delete[] ``` - **正确**:使用数组特化版本 ```cpp std::unique_ptr<int[]> arr(new int[10]); // ✅ 析构时调用 delete[] ``` 3. **自定义删除器兼容性** - **问题**:删除器类型不匹配导致未定义行为 ```cpp auto deleter = [](int* p) { delete[] p; }; std::unique_ptr<int, decltype(deleter)> ptr(new int[5], deleter); // ❌ 错误:new/delete 与 new[]/delete[] 不匹配 ``` - **正确**:确保删除器与分配方式匹配 ```cpp std::unique_ptr<int[], decltype(deleter)> ptr(new int[5], deleter); // ✅ ``` --- #### 三、高级注意事项 1. **与 STL 容器配合** - **移动语义要求**:插入 `unique_ptr` 到容器时必须使用 `std::move` ```cpp std::vector<std::unique_ptr<int>> vec; vec.push_back(std::make_unique<int>(10)); // ✅ vec.push_back(*new std::unique_ptr<int>(20)); // ❌ 错误:无法拷贝 ``` 2. **多态类型管理** - **安全向下转型**:需使用 `dynamic_cast` ```cpp class Base { virtual void foo() {} }; class Derived : public Base {}; std::unique_ptr<Base> b = std::make_unique<Derived>(); auto d = dynamic_cast<Derived*>(b.get()); // ✅ 安全检查 ``` 3. **线程安全** - **所有权转移安全**:跨线程传递需显式移动 ```cpp std::unique_ptr<Data> ptr = std::make_unique<Data>(); std::thread t([p = std::move(ptr)]() { /* 所有权转移至线程 */ }); t.join(); ``` --- #### 四、最佳实践 1. **优先使用 `make_unique`** - **优势**:避免裸指针暴露,确保异常安全 ```cpp auto ptr = std::make_unique<Widget>(arg1, arg2); // ✅ 推荐 auto ptr2(new Widget()); // ❌ 不推荐(可能引发异常安全问题) ``` 2. **避免循环引用** - **场景**:树形结构中父节点与子节点的引用 ```cpp struct Node { std::unique_ptr<Node> child; Node* parent; // 子节点通过原始指针引用父节点 }; ``` 3. **资源泄漏预防** - **断言检查**:确保资源在预期时间释放 ```cpp auto ptr = std::make_unique<int>(42); assert(ptr != nullptr); // 非空检查 ptr.reset(); // 显式释放 assert(ptr == nullptr); // 空指针检查 ``` 4. **异常安全** - **自动释放机制**:即使抛出异常,资源仍能释放 ```cpp void risky_function() { auto ptr = std::make_unique<Resource>(); // 可能抛出异常 } // 资源自动释放 ``` --- ### ❗️总结表格:关键注意事项速查 | 问题类型 | 错误示例 | 正确做法 | |------------------|----------------------------------|----------------------------------| | 拷贝所有权 | `auto p2 = p1;` | `auto p2 = std::move(p1);` | | 数组资源管理 | `unique_ptr<int>` 管理数组 | 使用 `unique_ptr<int[]>` | | 自定义删除器 | `delete` 与 `delete[]` 混用 | 确保删除器与分配方式匹配 | | 容器插入 | 直接拷贝插入 | 使用 `std::move` 或 `make_unique` | | 多线程传递所有权 | 直接共享 `unique_ptr` | 通过移动语义转移所有权 | ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值