c++智能指针

本文介绍了C++中智能指针的概念及其应用,包括Auto_ptr、Scoped_ptr、Shared_ptr和Weak_ptr等,并详细阐述了它们如何通过RAII理念解决资源管理和内存泄漏问题。

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

c++智能指针**
智能指针是什么?智能指针运用了一种思想->RAII(资源分配即初始化)
资源分配及初始化:定义一个类来封装资源的分配和释放,在构造函数初始化对象,析构函数释放对象,可以正确的初始化和释放。

为什么出现智能指针,智能指针能够有效地解决异常安全问题,或者资源未被释放,而导致内存泄漏。例如:中断语句导致代码跳出,导致内存未被释放。

常见的智能指针有哪些?Auto_ptr,Scoped_ptr,Shared_ptr,Weak_ptr;

c++发展在c++98标准时出现了Auto_ptr(不完善的智能指针):即两个指针无法共同管理一块相同的空间(管理权的转移);接着在c++发展的过程中出现了boost库,boost库中完善了Auto_ptr所不完全的功能出现了Scoped_ptr(防拷贝指针);Shared_ptr(引用计数);Weak_ptr(不计数,与shared_ptr结合使用);

Auto_ptr:

template<class T>
class Autoptr
{
public:
    Autoptr(T* ptr = NULL)
        :_ptr(ptr)
    {}
    Autoptr(Autoptr<T>& ap)//拷贝构造
    {
        _ptr = ap._ptr;
        ap._ptr = NULL;
    }
    Autoptr<T>& operator=(Autoptr<T>& ap)//赋值操作符重载(管理权的转移)
    {
        if(this != &ap)
        {
            delete _ptr;
            _ptr = ap._ptr;
            ap._ptr = NULL;
        }
        return *this;
    }
    T& operator*()//重载*操作符
    {
        return *_ptr;   
    }
    T* operator->()//重载->操作符
    {
        return _ptr;    
    }
    ~Autoptr()//析构函数
    {
        cout<<"~Autoptr()"<<endl;
        delete _ptr;
    }
private:
    T* _ptr;
};

由于Auto_ptr无法实现多个指针共同管理一块内存空间,因此被称为失败的作品;在boost库中产生了Scoped_ptr,C++11中称为unique_ptr原理差不多是相同的,在保护中只声明赋值操作符的重载函数,和拷贝构造函数;
Scoped_ptr:

template<class T>
class Scopedptr
{
public:
    Scopedptr(T* ptr = NULL)
        :_ptr(ptr)
    {}
    T& operator*()
    {
        return *_ptr;   
    }
    T* operator->()
    {
        return _ptr;    
    }
    ~Scopedptr()
    {
        cout<<"~Scopedptr()"<<endl;
        delete _ptr;
    }
protected:
    Scopedptr(Scopedptr<T>& ap);
    Scopedptr<T>& operator=(Scopedptr<T>& ap);
private:
    T* _ptr;
};

然而这两种指针没有解决多个指针共同管理一块内存空间,接着Shared_ptr采用引用计数的方法解决了这个问题,在Shared_ptr中定义一个整型的指针,多开辟一块空间,存放引用计数,解决在析构时,析构多次的问题。
Shared_ptr:

template <class T>
class Sharedptr
{
public:
    Sharedptr(T* ptr = NULL)
        :_ptr(ptr)
        ,_c(new int(1))
    {}
    Sharedptr(Sharedptr<T>& ap)
    {
        _ptr = ap._ptr;
        _c = ap._c;
        (*_c)++;
    }
    Sharedptr<T>& operator=(const Sharedptr<T>& ap)
    {
        if(_ptr != ap._ptr)
        {
            if((*_c)!=1)
            {
                *_c--;
                _ptr = ap._ptr;
                _c = ap._c;
                (*_c)++;
            }
            else
            {
                delete _ptr;
                _ptr = ap._ptr;
                _c = ap._c;
                (*_c)++;
            }
        }
        return *this;
    }
    T& operator*()
    {
        return *_ptr;   
    }
    T* operator->()
    {
        return _ptr;    
    }
    ~Sharedptr()
    {
        if((*_c)!=1)
        {
            _ptr = NULL;
            (*_c)--;
        }
        else
        {
            cout<<"~Sharedptr()"<<endl;
            delete _ptr;
            delete _c;
        }
    }
private:
    T* _ptr;
    int* _c;
};

Shared_ptr很好的解决了多个指针管理一块内存空间,然而出现了新的问题,循环引用的问题,这就要结合Weak_ptr来使用,举个例子:

struct ListNode
{
    Sharedptr<ListNode> _prev;
    Sharedptr<ListNode> _next;
    ListNode()
        :_prev(NULL)
        ,_next(NULL)
    {
    }
};
//假设用Shared_ptr来管理链表
int main()
{
    Sharedptr<ListNode> p1(new ListNode());
    Sharedptr<ListNode> p2(new ListNode());

    p1->_next = p2;
    p2->_prev = p1;
}
/*  这就让p1->next指向p2,p2的引用计数p2._c就会++,
    同理p1的引用计数p1._c就会++;这样会导致在析构时,无法析构,
    从而导致内存泄漏。*/

解决办法是什么呢?Weak_ptr很好的解决了这个办法:

struct ListNode
{
    Weak_ptr<ListNode> _prev;
    Weak_ptr<ListNode> _next;
    ListNode()
        :_prev(NULL)
        ,_next(NULL)
    {
    }
};
template<class T>
class Weak_ptr
{
public:
    Weak_ptr(T* ptr)
        :_ptr(ptr)
    {}
    ~Weak_ptr
    {
        delete _ptr;
    }
private:
    T* _ptr
}

解决了循环引用问题,还有个问题Shared_ptr中采用的释放方式时delete
若想开辟一块数组则采用delete[],当然内置类型不用delete[],也不会出错,但是若是开辟string类型的数组则会出现问题,这就要考虑到string的对象模型了,string会在开头多开辟四个字节用来存放个数,然而指针释放则不会向前便宜四个字节,因此又采用了仿函数,用来解决不是内置类型的释放。

仿函数:即在类中重载operator()。

template<class T>
struct less
{

    bool operator()(const T& a,const T& b)
    {
        return a < b;
    }
};

int main()
{
    cout<<less<int>()(12,22)<<endl;
    return 0;
}

上面是仿函数的一个例子,仿函数就是一个类中重载了operator(),可以像普通函数一样使用。

智能指针总结

  1. 智能指针采用了RAII的思想方法:在构造函数初始化对象,析构函数释放资源。
  2. 智能指针重载了operator*和operator->,能够像普通指针一样使用。
  3. 四个智能指针:Auto_ptr;Scoped_ptr;Shared_ptr;每个指针有各自的特点。注意:Shared_ptr,采用了Weak_ptr解决循环引用的问题,定制仿函数解决类型析构问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值