C++智能指针详解

本文介绍了C++中智能指针的概念及其发展历史,并详细解释了RAII思想的应用。通过模拟实现auto_ptr、scoped_ptr和shared_ptr,展示了智能指针如何帮助解决内存管理问题。此外,还探讨了weak_ptr的设计目的及其作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.智能指针的产生背景及发展历史:

    在C++中动态内存的管理由一对运算符来管理:new,为动态内存分配空间并返回一个指向该对象的指针;delete,接受一个动态对象的指针,并销毁该对象,释放与之相关联的内存。有时我们忘记释放内存,造成内存泄漏;有时在有指针引用内存的情况下就释放了它,在这种情况下会产生引用非法内存的指针。
RAII(一种解决问题的思想)
     RAII(Resource Acquisition Is Initialization),也称为为“资源获取就是初始化”,是C++语言的一种管理资源、避免泄漏的惯用法。C++标准保证任何情况下,已构造的对象最终会销毁,即它的析构函数最终会被调用。简单的说,RAII 的做法是使用一个对象,在其构造时获取资源,在对象生命期控制对资源的访问使之始终保持有效,最后在对象析构的时候释放资源。通过应用RAII思想,就产生了智能指针:
    智能纸指针的行为类似常规指针;重要的是它负责自动释放所指的对象。

发展历程:
这里写图片描述

2.模拟实现智能指针

1>模拟实现auto_ptr
template <class T>
class AutoPtr  
{
public:
    AutoPtr(T *ptr)
        :_ptr(ptr)
    {
    }
    ~AutoPtr()
    {
        if (_ptr != NULL)
        {
            delete _ptr;
            _ptr = NULL;
        }
    }
    AutoPtr(AutoPtr <T> & ap)
        :_ptr(ap._ptr)
    {
        ap._ptr = NULL; 
 //只能有一个对象的成员指针指向同一块区域,前一个对象的成员指针置为空指针
    }

    T& operator  //注意返回值问题不能返回一个临时变量,临时变量具有常性,会导致不能通过解引用更改值
    {
        return *(this->_ptr);
    }

    AutoPtr& operator=(AutoPtr &ap)
    {
        //防止自赋值
        if (this != &ap)
        {
            delete _ptr;
            this->_ptr = ap._ptr;
            ap._ptr = NULL;
        }
        return *this;
    }

    T*  operator->()
    {
        //this->_ptr->;
        return _ptr;
    }
private:
    T *_ptr;
};

class AA
{
public:
    int _a;
    int _b;
};


//测试用例

void TestAutoPtr()
{
    AutoPtr<int> ap1(new int);
    *ap1 = 10;
    AutoPtr<int> ap2(ap1); 
    //*ap1 = 20;  
    //这句代码会导致程序崩溃,因为当有两个对象的成员指针指向同一块区域
    //时,前一个对象的成员指针会被置成空指针 
    AutoPtr<int> ap3(new int(20));
    ap2 = ap3;
    AutoPtr<AA> ap4(new AA);
    ap4->_a = 10;
    ap4->_b = 20;
    ap2 = ap3;
}
2>模拟实现scoped_ptr(和unique_ptr功能类似)
template<class T>
class ScopedPtr   
{
public:
    ScopedPtr(T *ptr)
        :_ptr(ptr)
    {
    }

    ~ScopedPtr()
    {
        if (_ptr)
        {
            delete _ptr;
            _ptr = NULL;
        }
    }

    T& operator *()
    {
        return  *(_ptr);
    }

    T* operator ->()
    {
        return _ptr;
    }
protected:
    //构造和拷贝构造只声明,不定义
    ScopedPtr& operator=(const ScopedPtr & sp);
    ScopedPtr(const ScopedPtr & sp);
private:
    T* _ptr;
};

class AA
{
public:
    int _a;
    int _b;
};

//测试用例
void TestScopedPtr()
{

    ScopedPtr<int> ap1(new int);
    *ap1 = 10;
    ScopedPtr<int> ap3(new int(10));
//  ap3 = ap1;
//  ScopedPtr<int> ap4(ap1);
//上述两句代码编译不通过,因为ScopedPtr的构造和拷贝构造只声明,没定义,
//且为了防止别人进行修改定义为保护或者私有成员
    ScopedPtr<AA> ap2(new AA);
    ap2->_a = 10;
    ap2->_b = 20;
}
3>shared_ptr的模拟实现(仿函数版)(实际实现要复杂)

仿函数:

     仿函数(functor),就是使一个类的使用看上去象一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了。
    没有使用仿函数,就不能实现SharedPtr的多种功能,只使用delete删除的话,如果是new[]的空间,就会造成内存泄漏。

这里写图片描述

template<class T>
struct DeleteArray
{
    void operator()(T *ptr)
    {
        delete[] ptr;
    }

};

template<class T>
struct Delete
{
    void operator()(T *ptr)
    {
        delete ptr;
    }

};

struct Fclose
{
    void operator()(FILE * ptr)
    {
        fclose(ptr);
    }
};

template<class T,class Del>
class SharedPtr  
{
public:
    SharedPtr(T *ptr)
        :_ptr(ptr)
        , _reference_count(new int (1))
    {
    }
    ~SharedPtr()
    {
        Clear();
    }
    void Clear()
    {
        if (--(*_reference_count) == 0)
        {
             _del(_ptr);
            delete _reference_count;
            _reference_count = NULL;
        }

    }
    SharedPtr( SharedPtr<T,Del> & sp)
    {
        _ptr = sp._ptr;
        _reference_count = sp._reference_count;
        (*_reference_count)++;
    }

    //传统写法
    //SharedPtr& operator =(const SharedPtr<T,Del> & sp)
    //{
    //  if (this != &sp)
    //  {
    //      Clear();
    //      _ptr = sp._ptr;
    //      _reference_count = sp._reference_count;
    //      (*_reference_count)++;
    //  }
    //  return *this;
    //}

    //现代写法
    SharedPtr& operator =(SharedPtr<T,Del> sp)
    {
        swap(_ptr, sp._ptr);
        swap(_reference_count, sp._reference_count);
        return *this;
    }
    T& operator *() 
    {
        return *(this->_ptr);
    }

    T*  operator->()
    {
        return _ptr;
    }
private:
    T *_ptr;
    Del _del;
    int *_reference_count;
};


//测试用例
void TestSharedPtr()
{
    SharedPtr<int,Delete<int>> ap3(new int(10));
    SharedPtr<int,Delete<int>> ap4(ap3);
    SharedPtr<int,DeleteArray<int>> ap5(new int[10]);
    SharedPtr<FILE, Fclose>ap6(fopen("text.txt", "w"));
}
4>weak_ptr产生原因:
     shared_ptr虽然功能强大,但是存在一些问题,比如说循环引用问题。
struct ListNode
{
    shared_ptr<ListNode> _prev;
    //shared_ptr为库文件中实现的,只需包memory即可使用  
    shared_ptr<ListNode> _next;
    int data;
    ListNode(int x)
    {
        data = x;
        _prev = NULL;
        _next = NULL;
    }
    ~ListNode()
    {
        cout << "~ListNode()" << endl;
    }
};
int main()  
{  
    shared_ptr<ListNode> cur(new ListNode(1));
    shared_ptr<ListNode> next(new  ListNode(2));
    cur->_next = next;
    next->_prev = cur;
    return 0;        
}  

循环引用:
这里写图片描述
为了解决shared_ptr循环引用所带来的问题,就有了weak_ptr:

  weak_ptr是一种不控制所指对象生存周期的智能指针,它指向一个shared_ptr所管理的对象。将一个weak_ptr绑定到shared_ptr不会改变shared_ptr的引用计数。一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。weak_ptr的名字抓住了这种智能指针“弱”共享对象的特点。

3.智能指针的总结

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值