SmartPointer_智能指针

动态内存

C++中程序用来存储动态分配(dynamically allocate)的对象——即那些在程序运行时分配的对象。

动态内存的生存期由程序控制,也就是当动态对象不再使用时,我们必须显示的销毁它们。

But众所周知(王小波句式),正确的管理动态内存是非常棘手的。如果忘了释放内存,就会导致内存泄漏;如果在还有指针引用内存时就去释放那块内存,那么那个指针就会变为一个引用非法内存的指针。thorny problem!

智能指针原理

So, 为了更容易更安全的使用动态内存,新的标准库提供了智能指针(smart pointer)类型来管理动态对象。

智能指针的行为类似常规指针,区别是它负责自动释放所指向的对象。

那么smart pointer是如何做到自动释放内存的呢?

其实,智能指针是一个类类型,假如给它起个类名叫SmartPtr吧。在这个SmartPtr类中,有一个private成员变量,这个变量是另外一种类类型的指针(就是说它是一个指针,指向一个类),然后通过这个指针来动态创建类的对象。

比如,这里创建了SmartPtr类和Animal类,SmartPtr中的成员变量ptr_为Animal类的一个指针。

代码如下:

#ifndef SMARTPTR
#define SMARTPTR 

#include <iostream>

//禁用赋值与复制
#define DISALLOW_COPY_AND_ASSIGN(TypeName) 
    TypeName(const TypeName&); 
    void operator=(const TypeName&)

class Animal
{
    public:

        Animal()
        {
            std::cout << "construct......." << std::endl;
        }

        ~Animal()
        {
            std::cout << "destruct ... " << std::endl;
        }
        void run()
        {
            std::cout << "running......." << std::endl;
        }
};


class SmartPtr
{
    public:
        //SmartPtr针对的是heap上的对象
        SmartPtr(Animal *ptr);
        ~SmartPtr();

        Animal &operator*();
        const Animal &operator*() const;

        Animal *operator->(); //重载SmartPtr这个类的->
        const Animal *operator->() const;
    private:
        DISALLOW_COPY_AND_ASSIGN(SmartPtr);
        Animal *ptr_; 
};

#endif  /*SMARTPTR*/

SmartPtr::SmartPtr(Animal *ptr)
    :ptr_(ptr)
{
}

SmartPtr::~SmartPtr()
{
    delete ptr_; //SmartPtr析构时释放指向Animal类的指针,从而释放内存
}

Animal &SmartPtr::operator*()
{
    return *ptr_;
}

//const 版本 const Animal &SmartPtr::operator*() const { return *ptr_; } Animal *SmartPtr::operator->() { return ptr_; } //const版本 const Animal *SmartPtr::operator->() const { return ptr_; }

 然后我们编写一个异常处理函数来测试一下:

#include <stdexcept>
#include "SmartPtr.hpp"
using namespace std;

int main(int argc, const char *argv[])
{
    try{
        //这里的ptr离开try时一定会被销毁
        //从而导致Animal对象一定会被析构
        SmartPtr ptr(new Animal);
        throw runtime_error("error");

    }catch(runtime_error &e)
    {
        cout << e.what() << endl;
		/*
		construct...
		destruct...
		error
		*/
    }
    return 0;
}

这里补充一点:在异常处理函数中,如果在栈展开过程中退出了某个块(执行到throw语句),编译器将会负责确保在这个块中的对象能被正确的销毁。如果某个局部对象的类型是类类型,则该对象的析构函数将会被自动调用。

所以,上面程序中当执行到throw语句时,直接跳到catch中继续运行。那么原来try中的东东会确保被销毁,也就是编译器自动会去调用ptr这个对象的析构函数,从而执行了delete ptr_, 所以存储Animal对象的内存就被自动释放了。

一句话,智能指针利用了栈对象的生存期,将资源的获取放在构造函数里面,资源的释放放在析构函数里面,从而保证了资源一定会被正确释放。

最后,这也就是C++中的RAII(Resource Acquirement Is Initialization)

RAII要求,资源的有效期与持有资源的对象的生命期严格绑定,即由对象的构造函数完成资源的分配(获取),同时由析构函数完成资源的释放。在这种要求下,只要对象能正确地析构,就不会出现资源泄露问题。

原文地址:https://www.cnblogs.com/beatrice7/p/4123737.html