C++中的智能指针(一):std::auto_ptr<class T> - <memory>

一:内存泄漏的问题

考虑如下的程序

void func()
{
    ClassA *a = new ClassA;
    ...
    delete a;
}

应该使用delete语句以保证new分配的空间一定会被释放。我几乎总会忘记delete语句,尤其函数要写return语句时,更容易忘记。

另外即使你加上了delete语句,你也无法完全避免空间无法释放的问题(这种问题的统一叫法:内存泄漏)。

void func()
{
    ClassA *a = new ClassA;
    ...
    //产生异常
    delete a;
}

如果在这个函数执行时发生异常,依照C++处理异常的规则,会立刻退出当前函数,返回到其调用函数以期望异常能够被捕捉并处理。由于函数直接结束,delete语句甚至都没有机会执行,空间仍然释放不了。当然可以采用如下的方法

void func()
{
    try{
        ClassA *a = new ClassA;
        ...
        //发生异常
        ...
    }
    catch(...){        //捕捉该异常
        delete a;
        throw;        //重新抛出该异常
    }
}

但如果你的程序可能抛出多个异常,或者使用了多个new来分配空间呢?会增加代码量。C++为了解决这个问题,设计了一种智能指针auto_ptr,虽然在C++11中已经被淘汰,但是它的思想值得学习一下。

二:使用auto_ptr解决异常发生时的内存泄漏问题

异常发生时,会自动在当前函数中查找捕捉该异常的语句。如果在当前函数中没有找到,就要清除当前函数栈空间中所有的局部变量然后在其调用函数找。局部变量的清除方法:

  1. 基本变量:直接释放所占用的空间
  2. 类:调用其析构函数

普通的指针只是基本变量,只保存内存空间的地址。于是在自身被删除时,所指向的空间的地址也没有了。系统无法再找到那块内存(当然是只有一个指针指向该空间),也就无法释放。

那,不如把指针包装到类中,在其析构函数中,将内存空间释放即可。C++添加了auto_ptr实现这种想法。本文最后是auto_ptr的定义,这里看下auto_ptr的仅有的属性和析构函数。

私有元素:

private:
    _Ty *_Myptr;    // the wrapped object pointer

这个指针就是auto_ptr用来保存内存空间地址的地方。

析构函数

~auto_ptr()
{    // destroy the object
    delete _Myptr;
}

delete语句可以确保这块空间在auto_ptr对象被清除时被释放掉。这可以解决异常发生时内存泄漏的问题。

于是,下面的程序也不再需要delete语句了。

void f()
{
    std::auto_ptr<ClassA> i_ptr(new ClassA);
    ...
}

三:auto_ptr的所有权概念

除了确保不会发生内存泄漏问题之外,auto_ptr还定义了一种严格的概念:拥有权(OwnerShip)。auto_ptr对于它所指的对象(或者说内存空间)有拥有权,并且不应该与任何其他的auto_ptr共享这个对象。即不应该出现多个auto_ptr指向一个对象的情况。当然,这种情况本身不可避免,因为auto_ptr在通过指针来创建时,是无法知道该指针是否已被其他的auto_ptr对象所使用。可以看它的构造函数如下:

构造函数一:

explicit auto_ptr(_Ty *_Ptr = 0) throw(): _Myptr(_Ptr){}    // construct from object pointer

使用一个普通指针初始化auto_ptr,不可以隐式调用,另外在这个构造函数中,并不存在对_Ptr进行检查的机制。 如下所示

int *pi1 = new int;

auto_ptr<int> ap1 = pi1;  //error,explicit初始化构造函数无法被隐式调用
auto_ptr<int> ap2(pi1);   //ok, ap1指向pi1所指空间
auto_ptr<int> ap3(pi1);   //还是ok,直接通过new操作符返回的指针初始化,该空间被初始化为23

所以在使用时,我们应该竭力避免这种使用一个指针初始化两个auto_ptr对象的情况。既然你要用auto_ptr对象指向你的对象,又何必再用普通指针呢?不如直接这样

auto_ptr<int> p(new int(234));

对象p指向这块新开辟的空间,以后也只使用p来指代它。

来说回OwnerShip。由于一个对象(或内存空间)只能被一个auto_ptr对象所指,要达到这样的要求,复制构造函数和赋值操作符又当如何操作,直接复制_Myptr对象吗?这样其实还是会导致多个auto_ptr指向同一个对象的情况。解决办法很简单,就是让这两类函数做好移交所有权的工作。如下所示

构造函数二(复制构造函数):

template<class _Other>
auto_ptr(auto_ptr<_Other>& _Right) throw(): _Myptr(_Right.release()){}// construct by assuming pointer from _Right

auto_ptr::release()功能为返回自身所指对象的地址,并将自身_Myptr设为空指针。源码如下

_Ty *release() throw()
{    // return wrapped pointer and give up ownership
    _Ty *_Tmp = _Myptr;
    _Myptr = 0;
    return (_Tmp);
}

那么复制构造函数的作用就是,新的auto_ptr指向_Right所指的对象,然后把_Right设置成空指针(即不指向任何对象)。就类似于交出了该被指对象的所有权。同时这个复制构造函数的目的也是为了让派生类的指针可以转换成基类的指针。

struct A{
    int a;
    A(int _a=0):a(_a){}
};
struct B:A{
    char b;
    B(int _a=0, char _b=‘\0‘):A(_a), b(_b){}
};
auto_ptr<B> pb(new B(3,‘B‘));
cout << pb.get() << endl;                //pb原来不为NULL
auto_ptr<A> pa(pb);
cout << pa->a << " "  << endl;           //可以用在派生类指针到基类指针的转换
cout << pb.get() << endl;                //pb现在为NULL

而三个重载的赋值操作符:

template<class _Other>
_Myt& operator=(auto_ptr<_Other>& _Right) throw()
{    // assign compatible _Right (assume pointer)
    reset(_Right.release());                //reset()的功能,查看源码
    return (*this);
}

_Myt& operator=(_Myt& _Right) throw()
{    // assign compatible _Right (assume pointer)
    reset(_Right.release());
    return (*this);
}

_Myt& operator=(auto_ptr_ref<_Ty> _Right) throw()
{    // assign compatible _Right._Ref (assume pointer)
    _Ty *_Ptr = _Right._Ref;
    _Right._Ref = 0;    // release old
    reset(_Ptr);    // set new
    return (*this);
}

其中也有 _Right.release() 或者 _Right._Ref = 0 ,说明_Right经过赋值运算后,最终都成为空指针,同样交出了拥有权(OwnerShip)。

构造函数二:

typedef auto_ptr<_Ty> _Myt;auto_ptr(_Myt& _Right) throw(): _Myptr(_Right.release()){}   // construct by assuming pointer from _Right auto_ptr

通过另一个auto_ptr对象,构造当前对象,且两个对象类型相同。

int *i = new int(234);
auto_ptr<int> p1(i);    //使用第一个构造函数
auto_ptr<int> p2(p1);   //使用第二个构造函数

构造函数三:

template<class _Ty>
struct auto_ptr_ref
{    // proxy reference for auto_ptr copying
    explicit auto_ptr_ref(_Ty *_Right)
        : _Ref(_Right)
    {    // construct from generic pointer to auto_ptr ptr
    }

    _Ty *_Ref;    // generic pointer to auto_ptr ptr
};

auto_ptr(auto_ptr_ref<_Ty> _Right) throw()
{    // construct by assuming pointer from _Right auto_ptr_ref
    _Ty *_Ptr = _Right._Ref;
    _Right._Ref = 0;    // release old
    _Myptr = _Ptr;            // reset this, _Myptr是auto_ptr的私有属性
}    

通过auto_ptr_ref来构造一个对象,它的意义是什么,我也不太清楚。使用方法如下

int *i =new int(32412);
auto_ptr_ref<int> refi(i);
auto_ptr<int> pi(refi);
cout<<pi.get() << " " << *pi<<endl;

其他的,包括复制构造函数和特殊的赋值运算符等,放到后面介绍。先介绍下,auto_ptr的所有权的概念。

三个普通方法

_Ty *get() const throw()
{    // return wrapped pointer
    return (_Myptr);
}

void reset(_Ty *_Ptr = 0)
{    // destroy designated object and store new pointer
    if (_Ptr != _Myptr)
        delete _Myptr;
    _Myptr = _Ptr;
}

get()方法会返回当前auto_ptr所指向的空间地址。

release()与get()一样,但是执行后,当前对象不再指向任何空间。

reset()方法会释放当前所指的空间,重新指向_Ptr指定的空间。

特殊操作符

重载的operator = ()

template<class _Other>
_Myt& operator=(auto_ptr<_Other>& _Right) throw()
{    // assign compatible _Right (assume pointer)
    reset(_Right.release());
    return (*this);
}

将_Other类型的auto_ptr赋值给_Ty类型的auto_ptr,这和复制构造函数一样,可以将派生类指针转换成父类指针。但是需要注意的是,指针移交的方式,并不是

this._Myptr = _Right._Myptr;

如果采用这种方法,在当前auto_ptr被清除时,delete会将_Myptr所指空间删除。如果此时尚有其他的指针也指向相同的空间,就很有可能重复调用delete来删除同一块地址空间多次。对一块地址空间执行多次delete的后果是无法预知的,所以应该给auto_ptr中加上某种限制,可以确保在auto_ptr被清除时,所指空间不会被delete多次。

很直接的想法就是,不要让别人也指向这块空间,如果auto_ptr指向这块空间,其他指针或者就不应该指向这块空间。

auto_ptr源码(摘自visual studio 2010 的 <memory>文件)

auto_ptr - <memory>

原文地址:https://www.cnblogs.com/mindulmindul/p/12152908.html

时间: 2024-07-30 14:23:33

C++中的智能指针(一):std::auto_ptr<class T> - <memory>的相关文章

C++中的智能指针

众所周知.C++中对堆内存的申请与释放全然由用户来控制,这就造成用户在使用的时候常常造成内存泄漏.野指针.反复释放等常见的挂掉问题,所以我们有必要提供一套机制.使得用户仅仅需申请对应的内存,不用管释放的问题,事实上这属于著名的RAII(Resource Acquisition Is Initialization)技术 .在C++中这样的技术称作"智能指针",C++中的智能指针技术越来越受到广泛应用.以下简要介绍下智能指针. 从以上描写叙述中能够看出,我们须要提供一套内存显式申请与隐式释

OSG中的智能指针

在OpenSceneGraph中,智能指针(Smart pointer)的概念指的是一种类的模板,它针对某一特定类型的对象(即Referenced类及其派生类)构建,提供了自己的管理模式,以避免因为用户使用new运算符创建对象实例之后,没有及时用delete运算符释放对象,而造成部分内存空间被浪费的后果,也就是所谓的内存泄露错误. 由于OSG中与场景图形有关的大多数类均派生自Referenced类,因此OSG大量使用了智能指针来实现场景图形节点的管理.智能指针的使用为用户提供了一种自动内存释放的

COCOS2D-X中的智能指针

Cocos2d-x中所有内存管理方式的基础是引用计数,动态分配一个Ref对象后其引用计数为1,并通过retain和release来增持和减少其引用计数.引用计数本身并不能帮助我们进行内存管理. 为了正确地释放对象的内存,Cocos2d-x使用Objective-C里面的自动回收池的机制来管理对象内存的释放.Autorelease有点类似于一个共享的"智能指针",该"智能指针"的作用域为一帧,该帧结束后,它将释放自己的引用计数,此时,如果该对象没有被其他"共

Android系统篇之----Android中的智能指针

一.前言 今天我们开启Android系统篇的文章了,其实一直想弄,只是之前一直没有太多深入的了解,最近又把这块拿出来好好看了一下,所以想从新梳理一下,来看看Android中的这块知识,首先我们今天来看一下:Android中的智能指针的概念,为什么说先看一下智能指针这个知识呢?因为我们在看Android源码的时候,会发现几乎好多地方都用到了这个东东,所以我们在介绍后面的知识点,先来看看这个吧. 二.问题 那么Android中的智能指针是个什么东西呢?我们知道Android用的Java语言开发的,J

Boost中的智能指针(转)

这篇文章主要介绍 boost中的智能指针的使用.(转自:http://www.cnblogs.com/sld666666/archive/2010/12/16/1908265.html) 内存管理是一个比较繁琐的问题,C++中有两个实现方案: 垃圾回收机制和智能指针.垃圾回收机制因为性能等原因不被C++的大佬们推崇, 而智能指针被认为是解决C++内存问题的最优方案. 1. 定义 一个智能指针就是一个C++的对象, 这对象的行为像一个指针,但是它却可以在其不需要的时候自动删除.注意这个“其不需要的

ATL和vc++中的智能指针(分别是CComPtr和_com_ptr_t)

一.智能指针的概念 智能指针是一个类,不是指针,智能指针在所包含的指针不再被使用时候会自动释放该所包含指针所占用的系统资源,而不用手动释放. 原理:智能指针封装了包含指针的AddRef()函数和Release()函数,且在该类不被需要的时候在析构函数里调用包含指针的Release()函数释放包含指针的资源.因此实质是利用类的析构达到调用包含指针的Release()函数的目的. 二.VC++中的智能指针:_com_ptr_t _com_ptr_t实质是一个类模板.使用它时需要提供三个参数:接口的名

c++中的智能指针auto_ptr解析

c++中的auto_ptr是一个类,却可以像指针一样去使用.使用auto_ptr需要包含头文件#include <memory> 例如:auto_ptr<string> ps(new string("hello"));可以像指针一样去使用它,可以这样cout << ps->size()<<endl;uto_ptr带来的好处是,程序员并不需要手动的去delete,这给容易忘记delete的程序员带来极大的好处,因为有时候delete真

实战c++中的智能指针unique_ptr系列-- 使用std::unique_ptr代替new operator(错误:‘unique_ptr’ is not a member of ‘std’)

写了很多篇关于vector的博客,其实vector很便捷,也很简单.但是很多易错的问题都是vector中的元素为智能指针所引起的.所以决定开始写一写关于智能指针的故事,尤其是unique_ptr指针的故事. 这是个开始,就让我们使用std::unique_ptr代替new operator吧! 还是用程序说话: #include<iostream> int main() { while (true) int *x = new int; } 看下任务管理器中的内存: 此时使用智能指针unique

C++中的智能指针(auto_ptr)

实际上auto_ptr 只是C++标准库提供的一个类模板,它与传统的new/delete控制内存相比有一定优势,使用它不必每次都手动调用delete去释放内存.当然有利也有弊,也不是完全完美的. 本文从下面的8个方面来总结auto_ptr使用的大部分内容. 1. auto_ptr是什么? auto_ptr 是C++标准库提供的类模板,auto_ptr对象通过初始化指向由new创建的动态内存,它是这块内存的拥有者,一块内存不能同时被分给两个这样拥有者(auto_ptr).当auto_ptr对象生命