码迷,mamicode.com
首页 > 其他好文 > 详细

智能指针

时间:2014-07-07 19:37:10      阅读:323      评论:0      收藏:0      [点我收藏+]

标签:style   blog   http   java   color   使用   

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 correctly
void 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 correctly
void 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: 1
Syaing:
Saying: Addition
~Simple: 1
Exception catched!

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

3. auto_ptr出错的情况

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

// Failure in using auto_ptr
void 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 == 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;
            }
        }
    }
};

智能指针,布布扣,bubuko.com

智能指针

标签:style   blog   http   java   color   使用   

原文地址:http://www.cnblogs.com/alway6s/p/3813362.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!