C++安全异常std:auto_ptr

本文详细介绍了C++智能指针auto_ptr的基本概念、安全应用以及常见误用场景。通过实例展示了如何利用auto_ptr避免内存泄漏、资源泄露等问题,同时警告了错误使用auto_ptr可能导致的严重后果。文章强调了正确理解和使用auto_ptr的重要性,旨在帮助开发者构建更加健壮、安全的代码。
auto_ptr它是C++标准库(<utility>)为了一个智能指针类模板来解决资源泄漏所提供的问题(注意:这只是一个简单的智能指针)
auto_ptr在事实原则的实现RAII,对资源的访问,在施工时间。释放资源时,析构函数,而相关的指针操作超载。使用起来就像普通的指针。


std::auto_ptr<ClassA> pa(new ClassA);

非常多人听说过标准auto_ptr智能指针机制,但并非每一个人都天天使用它。这真是个遗憾。由于auto_ptr优雅地攻克了C++设计和编码中常见的问题,正确地使用它能够生成健壮的代码。本文阐述了怎样正确运用auto_ptr来让你的代码更加安全——以及怎样避免对auto_ptr危急但常见的误用,这些误用会引发间断性发作、难以诊断的bug。

1.为什么称它为“自己主动”指针?
auto_ptr仅仅是众多可能的智能指针之中的一个。很多商业库提供了更复杂的智能指针。用途广泛而令人惊异,从管理引用的数量到提供先进的代理服务。能够把标准C++ auto_ptr看作智能指针的Ford Escort(elmar注:可能指福特的一种适合家居的车型):一个简易、通用的智能指针,它不包括全部的小技巧。不像专用的或高性能的智能指针那么奢华。可是它能够非常好的完毕很多普遍的工作,它非常适合日常性的使用。

auto_ptr所做的事情,就是动态分配对象以及当对象不再须要时自己主动运行清理。这里是一个简单的代码演示样例,没有使用auto_ptr所以不安全:
    // 演示样例 1(a): 原始代码
    //
    void f()
    {
      T* pt( new T );


      /*...很多其它的代码...*/


      delete pt;
    }


我们大多数人每天写类似的代码。

假设f()函数仅仅有三行而且不会有不论什么意外,这么做可能挺好的。

可是假设f()从不运行delete语句。或者是因为过早的返回。或者是因为运行函数体时抛出了异常,那么这个被分配的对象就没有被删除。从而我们产生了一个经典的内存泄漏。


能让演示样例1(a)安全的简单办法是把指针封装在一个“智能的”类似于指针的对象里,这个对象拥有这个指针而且能在析构时自己主动删除这个指针所指的对象。由于这个智能指针能够简单的当成一个自己主动的对象(这就是说,它出了作用域时会自己主动毁灭)。所以非常自然的把它称之为“智能”指针:


    // 演示样例 1(b): 安全代码, 使用了auto_ptr
    //
    void f()
    {
      auto_ptr<T> pt( new T );


      /*...很多其它的代码...*/


    } // 酷: 当pt出了作用域时析构函数被调用。
      // 从而对象被自己主动删除


如今代码不会泄漏T类型的对象。无论这个函数是正常退出还是抛出了异常。由于pt的析构函数总是会在出栈时被调用。清理会自己主动进行。


最后,使用一个auto_ptr就像使用一个内建的指针一样easy。并且假设想要“撤销”资源,又一次採用手动的全部权,我们仅仅要调用release():


    // 演示样例 2: 使用一个 auto_ptr
    //
    void g()
    {
      T* pt1 = new T;
      // 如今。我们有了一个分配好的对象


      // 将全部权传给了一个auto_ptr对象
      auto_ptr<T> pt2( pt1 );


      // 使用auto_ptr就像我们曾经使用简单指针一样
      *pt2 = 12;       // 就像 "*pt1 = 12;"
      pt2->SomeFunc(); // 就像 "pt1->SomeFunc();"


      // 用get()来获得指针的值
      assert( pt1 == pt2.get() );


      // 用release()来撤销全部权
      T* pt3 = pt2.release();


      // 自己删除这个对象,由于如今
      // 没有不论什么auto_ptr拥有这个对象
      delete pt3;


    } // pt2不再拥有不论什么指针,所以不要
      // 试图删除它...ok。不要反复删除


最后,我们能够使用auto_ptr的reset()函数来重置auto_ptr使之拥有还有一个对象。假设这个auto_ptr已经拥有了一个对象。那么,它会先删除已经拥有的对象,因此调用reset()就如同销毁这个auto_ptr,然后新建一个并拥有一个新对象:


    // 演示样例 3: 使用reset()
    //
    void h()
    {
      auto_ptr<T> pt( new T(1) );


      pt.reset( new T(2) );
        // 删除由"new T(1)"分配出来的第一个T


    } // 最后。pt出了作用域。
      // 第二个T也被删除了


2. 以下主要分析一下auto_ptr的几个要注意的地方:

1,Transfer of Ownership

auto_ptr与boost库中的share_ptr不同的,auto_ptr没有考虑引用计数,因此一个对象仅仅能由一个auto_ptr所拥有,在给其它auto_ptr赋值的时候,会转移这样的拥有关系。

#include <utility>
#include <iostream>
using namespace std;
 
class A
{
public:
    A() { id = ++count; cout << "create A" << id  <<  "\n"; }
    ~A() { cout << "destroy A" << id << "\n"; }
private:
    static int count;
    int id;
};
 
int A::count = 0;
 
/*调用该函数会丢失掉全部权*/
void sink(auto_ptr<A> a)
{
    cout << "Enter sink()\n";
}
 
/*调用该函数会创建对象,并获取全部权*/
auto_ptr<A> create()
{
    cout << "Enter create()\n";
    auto_ptr<A> a(new A());
    return a;
}
 
int main(int argc, char *argv[])
{
    auto_ptr<A> a1 = create();
    auto_ptr<A> a2 = a1; /*转移全部权,此时a1无效了*/
    auto_ptr<A> a3(new A());
    cout << "Exit create()\n";
    sink(a2);/*丢失全部权,会发现a2的释放在sink函数中进行*/
    cout << "Exit sink()\n";
    return 0;
}


 
输出结果是:<br>Enter create()<br>create A1<br>create A2<br>Exit create()<br>Enter sink()<br>destroy A1<br>Exit sink()<br>destroy A2<br><br>
2。从上可知因为在赋值。參数传递的时候会转移全部权,因此不要轻易进行此类操作。


   比方:std::auto_ptr<ClassA> pa(new ClassA());


   bad_print(pa); //丢失了全部权


   pa->...; //Error

怎样防止函数调用转移全部权呢?

在《the c++ standard library》page43里有提到,採用const  reference,这样形參不会交出全部权。由于无法令 const reference交出全部权!!

3,使用auto_ptr作为成员变量,以避免资源泄漏。


   为了防止资源泄漏。我们通常在构造函数中申请,析构函数中释放。可是仅仅有构造函数调用成功,析构函数才会被调用,换句话说,假设在构造函数中产生了异常,那么析构函数将不会调用。这样就会造成资源泄漏的隐患。




   比方,假设该类有2个成员变量,指向两个资源。在构造函数中申请资源A成功,但申请资源B失败,则构造函数失败。那么析构函数不会被调用。那么资源A则泄漏。




  为了解决问题,我们能够利用auto_ptr代替普通指针作为成员变量,这样首先调用成功的成员变量的构造函数肯定会调用其析构函数,那么就能够避免资源泄漏问题。


4,不要误用auto_ptr


  1)auto_ptr不能共享全部权,即不要让两个auto_ptr指向同一个对象。




  2)auto_ptr不能指向数组。由于auto_ptr在析构的时候仅仅是调用delete,而数组应该要调用delete[]。




  3)auto_ptr仅仅是一种简单的智能指针。如有特殊需求。须要使用其它智能指针,比方share_ptr。


  4)auto_ptr不能作为容器对象,STL容器中的元素常常要支持拷贝。赋值等操作,在这过程中auto_ptr会传递全部权。那么source与sink元素之间就不等价了。

參考:

http://www.cnblogs.com/qytan36/archive/2010/06/28/1766555.html

http://blog.youkuaiyun.com/cyblueboy83/article/details/1792463

3 c++ primer page591~

版权声明:本文博客原创文章,博客,未经同意,不得转载。

<think>首先,用户的问题是:“我想了解C++中智能指针std::weak_ptr的使用和特性 请问C++ std::weak_ptr 智能指针的用途和使用方法”。这是一个关于C++std::weak_ptr的问题。 系统级指令要求: - 所有行内数学表达式使用$...$格式。 - 独立公式使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识,例如[^1]。 - 回答结构清晰,帮助用户逐步解决问题。 - 保证回答真实可靠。 用户提供的引用包括: - [^1]: 关于shared_ptr和unique_ptr的使用场景。 - [^2]: std::weak_ptr的定义和示例代码。 - [^3]: std::weak_ptr用于打断环状引用。 - [^4]: 自定义智能指针示例。 - [^5]: std::weak_ptr用于临时所有权。 我需要基于这些引用构建回答,因为用户说“上述引用仅供参考”,但我应该参考用户的问题和我的回答。用户的问题是新的,所以我会基于标准知识和引用来回答。 关键点: - **std::weak_ptr** 是一个智能指针,它不拥有对象的所有权;它指向一个由std::shared_ptr管理的对象。 - **用途**: - 避免循环引用(例如,两个对象互相持有shared_ptr,导致内存泄漏)。 - 用于临时访问对象,而不增加引用计数。 - **使用方法**: - 从shared_ptr或另一个weak_ptr构造。 - 使用lock()方法来获取一个shared_ptr,从而访问对象。 - 不会增加引用计数,所以不会阻止对象被销毁。 从引用中: - [^2] 提供了示例代码,展示了循环引用的场景。 - [^3] 解释了用于打断环状引用。 - [^5] 解释了用于临时所有权。 回答结构: - 介绍std::weak_ptr。 - 解释用途。 - 解释特性。 - 提供使用方法,包括代码示例。 - 生成相关问题。 - 确保引用标识自然地添加。 由于是中文回答,所有内容用中文写。 步骤: 1. **介绍**:简要解释什么是std::weak_ptr。 2. **用途**:详细说明其用途。 3. **特性**:描述其特性,如不增加引用计数等。 4. **使用方法**:提供代码示例。 5. **总结**:简要总结。 6. **生成相关问题**:在最后添加相关问题。 引用标识:在相关段落末尾添加[^n],基于提供的引用。 例如: - 当提到避免循环引用时,引用[^3]。 - 当提到临时所有权时,引用[^5]。 - 当给出示例时,参考[^2]。 代码示例:用户提供了引用[^2]中的代码,但它是关于循环引用的。我可以使用那个示例来展示weak_ptr如何解决它。 完整回答: - 开头:直接回答问题。 - 主体:逐步解释。 - 结尾:相关问题。 确保LaTeX:这里没有数学表达式,所以可能不需要。但如果有,就使用$...$或$$...$$。 现在,构建回答大纲: 1. **什么是std::weak_ptr?** - 解释它是什么。 - 引用[^2]。 2. **用途** - 避免循环引用:解释为什么需要它,使用引用[^3]。 - 临时访问:解释临时所有权,使用引用[^5]。 3. **特性** - 不增加引用计数。 - 构造方式:从shared_ptr或weak_ptr。 - 方法:lock()来获取shared_ptr。 4. **使用方法** - 提供代码示例:展示问题(循环引用)和解决方案(使用weak_ptr)。 - 基于引用[^2]修改。 5. **注意事项** - 简要提到注意事项。 6. **相关问题** - 生成2-3个相关问题。 最后,添加引用标识。</think>### C++ std::weak_ptr 智能指针的用途和使用方法 `std::weak_ptr` 是 C++ 标准库中的一种智能指针,主要用于协助 `std::shared_ptr` 管理资源,而不拥有对象的所有权。它不会增加对象的引用计数,因此不会影响对象的生命周期。下面我将逐步解释其用途、特性及使用方法,帮助您全面理解。 #### 1. **std::weak_ptr 的用途** - **避免循环引用导致的资源泄漏**:当多个 `std::shared_ptr` 相互引用形成环状结构时(如对象 A 持有 B 的 `shared_ptr`,B 又持有 A 的 `shared_ptr`),引用计数无法归零,导致内存泄漏。`std::weak_ptr` 可以打断这种循环,因为它不增加引用计数[^3]。 - **临时访问共享资源**:在需要临时访问一个可能已被删除的对象时,`std::weak_ptr` 提供了一种安全方式。通过将其转换为 `std::shared_ptr`,可以临时延长对象生命周期(如果对象还存在),从而避免悬空指针问题[^5]。 #### 2. **std::weak_ptr 的特性** - **不拥有所有权**:`std::weak_ptr` 指向由 `std::shared_ptr` 管理的对象,但不会增加引用计数。这意味着对象可以被安全销毁,而 `weak_ptr` 不会阻止析构。 - **构造方式**:可以从 `std::shared_ptr` 或另一个 `std::weak_ptr` 构造,构造和析构过程不会影响引用计数。 - **关键方法**: - `lock()`:返回一个 `std::shared_ptr`,用于安全访问对象。如果原对象已被销毁,则返回空的 `shared_ptr`。 - `expired()`:检查对象是否已被销毁(引用计数为 0)。 - **生命周期管理**:当所有 `shared_ptr` 被销毁后,对象会被释放,`weak_ptr` 自动失效。 #### 3. **std::weak_ptr 的使用方法** 下面通过代码示例展示常见场景。假设有两个类 `A` 和 `B`,相互引用可能造成循环泄漏。使用 `std::weak_ptr` 解决此问题: **问题场景:循环引用导致内存泄漏** ```cpp #include <iostream> #include <memory> class B; // 前向声明 class A { public: std::shared_ptr<B> ptrB; // 使用 shared_ptr,可能导致循环引用 ~A() { std::cout << "A destroyed" << std::endl; } }; class B { public: std::shared_ptr<A> ptrA; // 使用 shared_ptr,形成环状引用 ~B() { std::cout << "B destroyed" << std::endl; } }; int main() { { std::shared_ptr<A> a = std::make_shared<A>(); std::shared_ptr<B> b = std::make_shared<B>(); a->ptrB = b; b->ptrA = a; // 循环引用,析构函数不会被调用,内存泄漏 } return 0; } ``` 运行此代码时,`A` 和 `B` 的析构函数不会执行,因为引用计数无法归零[^2]。 **解决方案:使用 std::weak_ptr 打破循环** ```cpp #include <iostream> #include <memory> class B; // 前向声明 class A { public: std::shared_ptr<B> ptrB; ~A() { std::cout << "A destroyed" << std::endl; } }; class B { public: std::weak_ptr<A> ptrA; // 改为 weak_ptr,避免增加引用计数 ~B() { std::cout << "B destroyed" << std::endl; } }; int main() { { std::shared_ptr<A> a = std::make_shared<A>(); std::shared_ptr<B> b = std::make_shared<B>(); a->ptrB = b; b->ptrA = a; // weak_ptr 不增加引用计数,打破循环 } // 作用域结束时,A 和 B 的析构函数会被调用 return 0; } ``` 在此版本中,`B::ptrA` 使用 `weak_ptr`,引用计数能正常归零,对象被正确销毁。输出将显示 "A destroyed" 和 "B destroyed"。 **临时访问示例** ```cpp #include <iostream> #include <memory> int main() { std::weak_ptr<int> wptr; // 创建 weak_ptr { std::shared_ptr<int> sptr = std::make_shared<int>(42); wptr = sptr; // 从 shared_ptr 构造 weak_ptr if (auto temp = wptr.lock()) { // 使用 lock() 获取临时 shared_ptr std::cout << "Value: " << *temp << std::endl; // 输出: Value: 42 } } // 离开作用域,shared_ptr 被销毁 if (wptr.expired()) { std::cout << "Object has been destroyed" << std::endl; // 输出此消息 } return 0; } ``` - `lock()` 方法返回一个 `shared_ptr`,如果对象存在,则临时延长生命周期;如果对象已销毁,则返回空指针。 - `expired()` 用于检查对象状态,避免访问无效内存[^5]。 #### 4. **注意事项** - 使用 `lock()` 时,必须检查返回的 `shared_ptr` 是否为空,否则可能访问已销毁对象。 - `std::weak_ptr` 不能直接解引用,必须通过 `lock()` 转换为 `shared_ptr`。 - 在实际项目中,优先使用标准库的智能指针(如 `std::shared_ptr` 和 `std::weak_ptr`),而非自定义实现,以确保健壮性[^4]。 #### 总结 `std::weak_ptr` 是处理共享资源生命周期的高级工具,特别适合解决循环引用和实现临时访问。通过结合 `std::shared_ptr`,它能提升内存管理的安全性和效率。在选择智能指针时,如果需多个指针共享所有权,用 `shared_ptr`;如果需临时跟踪或打破循环,用 `weak_ptr`[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值