首页 > 代码库 > 智能指针

智能指针

<style></style>

1. 为什么要智能指针?

由于C++语言没有自动内存回收机制,程序员每次new出来的内存都要手动delete。但是有时候可能程序员会忘记delete,也可能是因为流程太复杂,最终没有delte,也可能是因为异常的存在,导致程序过早的退出,没有执行delete。

用只能指针可以有效的解决这些问题。std::auto_ptr,boost::scope_ptr,boost::scoped_array,boost::intrusive_ptr。

智能指针之所以能自动释放内存,是因为智能指针实际上是一个栈对象,并非指针类型,在栈对象结束时,智能指针通过析构函数释放所有它管理的堆内存。所有稚嫩指针都重载了“operator->”操作符,直接返回对象的引用,用以操作对象。访问只能指针原来的方法是用“.”操作符,

访问智能指针包含的裸指针可以用get()函数。由于智能指针是一个对象,所以if(my_smart_object)永远为真,要判断智能指针的裸指针是否为空,需要判断: if(my_smart_object.get())。

智能指针包含reset()方法,如果不传递参数(或者传递NULL),则智能指针会释放前管理的内存,如果传递一个对象,则智能指针会释放当前对象,来管理新传入的对象。

定义一个测试用的辅助类:

class Simple {public:    Simple(int param = 0){        number = param;        cout << "Simple: " << number << endl;    }    ~Simple() {        cout << "~Simple: " << number << endl;    }    void Speaker()    {        cout << "Saying: " << info_exted.c_str() << endl;    }    std::string  info_exted;    int number;};

2. 正确使用auto_ptr

先看下正确使用auto_ptr指针的例子:

// Use auto_ptr correctlyvoid testAutoPtr() {    auto_ptr<Simple> my_memory(new Simple(1));      // Create an instance of Simple and memory allocated from heap;                                                     // using the auto_ptr my_memory to arrange it.    if (my_memory.get()) {                          // Check if the Simple instance was correctly created.        my_memory->Speaker();                       // Initial speaker: info_extend has nothing        my_memory.get()->info_exted = "Addition";   // Use the get() function to reteive the bare poiter and to add information to info_extend.        my_memory->Speaker();                       // Saying: Addition        (*my_memory).info_exted += " other";                my_memory->Speaker();                       // Saying: Addition other        my_memory->info_exted += " other again!";        my_memory->Speaker();                       // Saying: Addition other other again!    }}

首先,在testAutoPtr中,首先定义了一个auto_ptr对象,并且使用它来管理一块Simple对象的内存。在进行其他操作之前,先检查new Simple(1)是否成功,既是检查原始指针是否为空。通过一些列的操作来判断是否正确。然后进行各种操作,最后我们没用显示的使用delete来释放simpl对象内存,但是从输出可以看的到,析构函数还是被调用了的,因为在退出my_memory的作用域时,释放了其管理的堆内存。如果在testAutoPtr()中有异常抛出会发生什么呢? 可以这样修改一下testAutoPtr:

// Use auto_ptr correctlyvoid testAutoPtr() {    auto_ptr<Simple> my_memory(new Simple(1));          if (my_memory.get()) {                                  my_memory->Speaker();                               my_memory.get()->info_exted = "Addition";           my_memory->Speaker();                               (*my_memory).info_exted += " other";        throw new exception("This is an exception!"); //  throw an exception here        my_memory->Speaker();                               my_memory->info_exted += " other again!";        my_memory->Speaker();                           }}int main(){    try    {        testAutoPtr();    }    catch (...)    {        cout << "Exception catched! " << endl;    }}

最后程序运行的输出如下:

Simple: 1Syaing:Saying: Addition~Simple: 1Exception catched!

可见,智能指针的确自动调用了Simple类的析构函数。

3. auto_ptr出错的情况

auto_ptr不支持赋值函数,但是确没有明确的禁止使用“=”操作符。

// Failure in using auto_ptrvoid testAutoPtr2() {    auto_ptr<Simple> my_memory(new Simple(1));    if (my_memory.get()) {        auto_ptr<Simple> my_memory2;        my_memory2 = my_memory;  // error begins from here        cout << my_memory2->number << endl;        my_memory2->Speaker();        my_memory->Speaker();   // execution crashed here    }}

使用my_memory2 = my_memory后,my_memroy2完全剥夺了my_memory对Simple对象内存的所有权,导致my_memory被悬空,从而再之后使用时出错。

所以:切记,在使用auto_ptr时,最好不要使用"operator="操作符!!!

再看另一个例子:

void testAutoPtr3() {    auto_ptr<Simple> my_memory(new Simple(1));    if (my_memory.get()) {        my_memory.release();    }}

结果是指只有构造函数被调用,而析构函数并没有被调用,造成了内存泄漏!!!这个错误的根源就是对release函数的误解,这里的release函数其实只是释放对内存的使用权,而不是释放内存空间。假如我们不想等到自动内存对象在离开作用域时才释放内存空间,不想其一直占用着内存,正确的代码如下:

void testAutoPtr3() {    auto_ptr<Simple> my_memory(new Simple(1));    if (my_memory.get()) {        Simple *temp_memory = my_memory.release();        delete temp_memory;    }}

或者:

void testAutoPtr3() {    auto_ptr<Simple> my_memory(new Simple(1));    if (my_memory.get()) {        my_memory.reset();  // release the inner memory    }}

4. auto_ptr总结

  1. 尽量不要使用”operator=“,如果使用了,请不要再使用先前的对象。
  2. 记住release()函数不会释放对象,仅仅是归还所有权。
  3. 最好不用当成参数传递。
  4. 由于不能使用=操作符,由其管理的对象不能放入std::vector等容器中。

正是由于这些限制的存在,boost中改进了自动指针,实现了boost::scope_ptr,boost::scoped_array,boost::intrusive_ptr智能指针。关于这些智能指针的使用,可以参考《C++ 智能指针详解》。

最常用的智能指针:

  1. std::auto_ptr,有很多问题,不能用赋值操作符,不支持拷贝构造函数,但是复制和赋值时又不会报错。因为不能被复制,所以不能放入容器中。
  2. C++11引入的unique_ptr,也不支持复制和赋值,但是好在如果直接赋值会出错,实在要复制的话可以使用std::move。
  3. C++11或者boost中有shared_ptr,是基于引用计数的智能指针。可随意复制和赋值,知道内存的引用计数为0时,这个内存才被释放。

5. 自己实现auto_ptr

有些BT级别的面试官可能会要求你自己写一个auto_ptr类,简单的来讲,一个auto_ptr必须包含构造函数、拷贝构造函数、析构函数、重载的"operater*"、重载的"operater->" 。这里给出两个版本,目前都还没测试是否可用,有时间再测试一下。

第一个,不适用引用计数,简单的智能指针:

template<typename T>class m_auto_ptr {public:    m_auto_ptr(T *pt = 0) : _ptr(pt) {    }    ~m_auto_ptr() {        delete _ptr;            }    T& operator*() {        if (_ptr)            return *_ptr;        else            throw new exception("smart pointer points to NULL");    }    T* operator->() {        if (_ptr)            return _ptr;        else            throw new exception("smart pointer points to NULL");    }private:    T *_ptr;};

第二个版本,使用引用计数的方法,如下:

template<typename T>class SmartPointer {public:    SmartPointer(T *pt = 0) : _ptr(pt), _ref_count(new int) {        if (pt)            *_ref_count = 1;        else             *_ref_count = 0;    }    SmartPointer(const SmartPointer& src) {        if (&src != this) {            _ptr = src._ptr;            _ref_count = src._ref_count;;            (*_ref_count)++;                    }           }    ~SmartPointer() {        (*_ref_count)--;        if ((*_ref_count) == 0) {            delete _ref_count;            delete _ptr;        }    }    T* get() {        if (_ptr)            return _ptr;        else            throw new exception("smart pointer points to NULL");    }    T& operator*() {        if (_ptr)            return *_ptr;        else            throw new exception("smart pointer points to NULL");    }    T* operator->() {        if (_ptr)            return _ptr;        else            throw new exception("smart pointer points to NULL");    }    SmartPointer& operator=(const SmartPointer& src) {        if (&src =http://www.mamicode.com/= this)            return *this;        else {            releaseCount();            _ptr = src._ptr;            _ref_count  = src._ref_count;            (*_ref_count)++;            return *this;        }    }private:    T *_ptr;    int *_ref_count;    void releaseCount() {        if (_ptr) {            (*_ref_count)--;            if ((*_ref_count) == 0) {                delete _ptr;                delete _ref_count;            }        }    }};