用auto_ptr类模板帮助动态内存管理

动态内存使用最多的是在C++应用程序的代码中。有过编程经验的程序员虽然都知道new操作符的使用一定要与delete匹配,在某些场合仍然可能有内存溢出。当异常被掷出时,程序的正常控制流程被改变,因此导致潜在的内存溢出。例如,
void g() //可能掷出
{
    if (some_condition == false)
      throw X();
}

void func() 
{
    string * pstr = new string;
    g(); //如果 g 掷出一个异常,内存溢出
    delete pstr; //如果 g 掷出一个异常,则此行为不能达到的代码行。
}
int main()
{
    try
    {
      func();
    }
    catch(...)
    {}
}
  当 g 掷出一个异常,异常处理机制展开堆栈:g()退出,同时控制被转移到 main() 的 catch(...)代码块。这时,无论怎样,func()中的delete语句都不会被执行,由此导致pstr的内存溢出。要是使用局部自动串变量,而不是使用动态分配-内存溢出就不会出现了:
string str; //局部自动对象
g(); //没有内存溢出

  许多数据重要的结构以及应用,象链表,STL容器,串,数据库系统以及交互式应用必须使用动态内存分配,因此仍然冒着万一发生异常导致内存溢出的风险。C++标准化委员会意识到了这个漏洞并在标准库中添加了一个特殊的类模板,它就是std::auto_ptr,其目的是促使动态内存和异常之前进行平滑的交互。Auto_ptr保证当异常掷出时分配的对象(即:new操作符分配的对象)能被自动销毁,内存能被自动释放。下面我们就来讨论使用动态内存时,如何正确和有效地使用auto_ptr来避免资源溢出。这个技术适用于文件,线程,锁定以及与此类似的资源。
  Auto_ptr的定义可以在<memory.h>中找到。与标准库中其它的成员一样,它被声明在命名空间std::中。当你实例化auto_ptr对象时,对它进行初始化的方法是用一个指针指向动态分配的对象,下面是实例化和初始化auto_ptr对象的例子:

#include <memory>
#include <string>
using namespace std;
void func()
{
    auto_ptr<string> pstr (new string); /* 创建并初始化auto_ptr */
}
  auto_ptr后面的尖括弧里指定auto_ptr指针的类型,在这个例子中是string。然后auto_ptr句柄的名字,在这个例子中是pstr。最后是用动态分配的对象指针初始化这个实例。注意你只能使用auto_ptr构造器的拷贝,也就是说,下面的代码是非法的:
auto_ptr<string> pstr  = new string; //编译出错
Auto_ptr是一个模板,因此它是完全通用的。它可以指向任何类型的对象,包括基本的数据类型:
auto_ptr<int> pi  (new int);
一旦你实例化一个auto_ptr,并用动态分配的对象地址对它进行了初始化,就可以将它当作普通的对象指针使用,例如:
*pstr = "hello world"; //赋值
  pstr->size(); //调用成员函数
之所以能这样做是因为auto_ptr重载了操作符&,*和->。不要被语法误导,记住pstr是一个对象,不是一个指针。

    auto_ptr是如何解决前面提到的内存溢出问题呢?auto_ptr的析构函数自动摧毁它绑定的动态分配对象。换句话说,当pstr的析构函数执行时,它删除构造pstr期间创建的串指针。你绝不能删除auto_ptr,因为它是一个本地对象,它的析构函数是被自动调用的。让我们看一下函数func()的修订版本,这次使用了auto_ptr:
void func() 
  {
    auto_ptr<string>  pstr (new string);
    g();  //如果g()掷出异常,pstr 被自动摧毁 
  }
  C++保证在堆栈展开过程中,自动存储类型的对象被自动摧毁。因此,如果g()掷出异常,pstr的析构函数将会在控制被转移到catch(...)块之前执行。因为pstr的析构函数删除其绑定的串指针,所以不会有内存溢出发生。这样我们在使用动态分配对象时,利用auto_ptr就实现了自动和安全的本地对象。

如何避免使用auto_ptr的缺陷

  auto_ptr并不是完美无缺的,它的确很方便,但也有缺陷,在使用时要注意避免。首先,不要将auto_ptr对象作为STL容器的元素。C++标准明确禁止这样做,否则可能会碰到不可预见的结果(在另文中讨论)。

    auto_ptr的另一个缺陷是将数组作为auto_ptr的参数:
auto_ptr<char>  pstr (new char[12] ); //数组;为定义
  记住不管什么时候使用数组的new操作时,必须要用delete[]来摧毁数组。因为auto_ptr的析构函数只对非数组类型起作用。所以数组是不能被正确摧毁的话,程序的行为是不明确的。总之,auto_ptr控制一个由new分配的单对象指针,仅此而已。 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
auto_ptr是C++98中提供的指针容器,用于管理动态分配的对象。auto_ptr的特点是具有唯一性,即同一时刻只能有一个auto_ptr指向同一块堆内存,当auto_ptr被销毁时,它所管理的对象也会被自动销毁。 auto_ptr的实现主要依赖于析构函数和移动语义。auto_ptr的析构函数会自动释放它所管理的对象,而移动语义则用于将auto_ptr的所有权转移给另一个auto_ptrauto_ptr的实现可以简单地通过一个模板类来实现,模板参数为auto_ptr所管理的对象类型。auto_ptr类中需要重载赋值运算符、析构函数、拷贝构造函数和移动构造函数等。下面是一个简单的auto_ptr实现示例代码: ```c++ template <typename T> class auto_ptr { public: explicit auto_ptr(T* p = nullptr) : ptr(p) {} ~auto_ptr() { delete ptr; } auto_ptr(auto_ptr<T>&& other) { ptr = other.ptr; other.ptr = nullptr; } auto_ptr& operator=(auto_ptr<T>&& other) { if (this != &other) { delete ptr; ptr = other.ptr; other.ptr = nullptr; } return *this; } T* operator->() const { return ptr; } T& operator*() const { return *ptr; } private: T* ptr; }; ``` 在上面的代码中,auto_ptr的构造函数和析构函数分别用于初始化和销毁auto_ptr所管理的对象。移动构造函数和赋值运算符用于将auto_ptr的所有权转移给另一个auto_ptr,同时更新指针的值。重载箭头运算符和解引用运算符用于访问auto_ptr所管理的对象。 需要注意的是,auto_ptr已经被C++11标准弃用,推荐使用unique_ptr和shared_ptr来代替auto_ptr。unique_ptr和shared_ptr都具有多个指针共享同一块堆内存的能力,并且使用了更为安全和高效的实现方式。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值