C++智能指针

智能指针:存储指向动态分配对象指针的类。
满足的条件:具有RAII思想,能够像指针一样(运算符的重载,解引用,指向对象成员),正确的资源管理。

RAII思想(资源分配及初始化):定义一个类来封装资源的分配和释放,构造函数中完成资源的分配和初始化,析构函数中完成资源的清理,可以保证资源的正确初始化和释放。

引入智能指针的目的是什么呢?
这里给出一段代码:

void func()
{
    int *_ptr = new int(3);
    if (_ptr)
    {
        throw 3;
    }
    delete _ptr;
}
int main()
{
    try
    {
        func();
    }
    catch (...)
    {}
    return 0;
}

上述代码在func函数中new一个四字节空间并进行初始化,接下来if条件判断语句为真,抛出异常,main函数直接捕获异常,函数返回0,导致开辟的空间没有被释放,造成内存泄漏。

其实只要在throw之前加一个delete语句就可以解决问题,但是重点是当代码超级多的时候,肯定会忘记的。那么有没有一种方法是可靠的呢——”智能指针“。

智能指针的发展历史

C++98 : auto_ptr 在构造对象的时候获取对象的管理权(管理权转移),
带有缺陷。“不提倡使用”
boost库(非官方):scoped_ptr 防拷贝的守卫指针(简单粗暴)。—scoped_array
shared_ptr 加入引用计数的共享指针(复杂,有循环引用的缺陷)。—shared_array
weak_ptr 不能单独存在的弱指针(辅助解决shared_ptr循环引用的问题)
C++11 (官方库):沿用了boost库,将scoped_ptr改名为unique_ptr,shared_ptr和weak_ptr名字没变。

对operator*,operator->的理解

template<class T>
class Myclass
{
public:
    Myclass(T* ptr)
        :_ptr(ptr)
    {}
    Myclass(Myclass<T>& p)
    {}
    Myclass<T>& operator=(Myclass<T>& p)
    {}

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

    T* operator->()
    {
        return _ptr;
    }

    ~Myclass()
    {
        if (_ptr)
        {
            delete _ptr;
        }
    }

private:
    T* _ptr;
};
struct My
{
    int a = 3;
    int b = 6;
};
int main()
{
    Myclass<int> p1(new int);
    *p1 = 20;
    int tmp = p1.operator*();
    Myclass<My> p2(new My);
    cout << "p1: " << *p1 << endl << endl;
    cout << "tmp: " << tmp << endl << endl;

    cout << "p2->a: " << p2->a << endl << endl;
    cout << "p2.operator->()->a: " << p2.operator->()->a << endl << endl;
    cout << "p2->b: " << p2->b << endl << endl;
    cout << "p2.operator->()->b: " << p2.operator->()->b << endl << endl;
    system("pause");
}

这里写图片描述
这里写图片描述

详解auto_ptr:
auto_ptr事实上是一个类,在构造时获取对象的管理权,不用考虑啥时候释放动态开辟的空间,在析构函数中直接释放,不会出现内存泄露的问题。

//auto_ptr的模拟实现
template <class T>
class Auto_ptr
{
public:
    Auto_ptr(T* ptr)//构造函数
        :_ptr(ptr)
    {}
    Auto_ptr(Auto_ptr<T>& ap)//拷贝构造函数
        : _ptr(ap._ptr)
    {
        ap._ptr = NULL;
    }
    Auto_ptr<T>& operator=(Auto_ptr<T>& ap)//赋值运算符的重载
    {
        if (this != &ap)
        {
            if (_ptr)
            {
                delete _ptr;
            }
            _ptr = ap._ptr;
            ap._ptr = NULL;
        }
        return *this;
    }
    ~Auto_ptr()//析构函数
    {
        if (_ptr)
        {
            delete _ptr;
        }
    }
    T& operator*()
    {
        return *_ptr;
    }
    T* operator->()
    {
        return _ptr;
    }
private:
    T* _ptr;
};
struct AA
{
    int _a;
    int _b;
};
int main()
{
    Auto_ptr<int> ap(new int (3));
    *ap = 5;
    Auto_ptr<int> ap1(ap);
    Auto_ptr<int> ap3(ap);//一个auto_ptr被拷贝或被赋值后, 其已经失去对原对象的所有权指向NULL
    Auto_ptr<AA> ap2(new AA);
    ap2->_a = 2;
    ap2->_b = 3;
    return 0;
}

缺陷:
1.一个指针变量指向的空间不能由两个auto_ptr管理,不然会析构两次,程序崩溃;

//错误
int *ptr=new int(5);
auto_ptr<int> ap(ptr);
auto_ptr<int> ap(ptr);

2.auto_ptr不能用来管理数组,析构函数中用的是delete。

int *ptr=new int[6];
auto_ptr<int> ap(ptr);

详解scoped_ptr
scoped_ptr防拷贝(拷贝构造函数和赋值运算符的重载是只声明不实现;用private对其进行访问限定,防止在类外定义)直接明了。

//scoped_ptr的模拟实现
template <class T>
class Scoped_ptr
{
public:
    Scoped_ptr(T* ptr)
        :_ptr(ptr)
    {}

    ~Scoped_ptr()
    {
        if (_ptr)
        {
            delete _ptr;
        }
    }

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

    T* operator->()
    {
        return _ptr;
    }

private:
    Scoped_ptr(const Scoped_ptr<T>& sp);
    Scoped_ptr<T>& operator=(const Scoped_ptr<T>& sp);

    T* _ptr;
};
int main()
{
    Scoped_ptr<int> sp(new int(2));
    return 0;
}

缺陷:不能拷贝,管理的对象不能共享所有权,功能不全面。

scoped_array
scoped_array和scoped_ptr的功能是一样的,只是scoped_array管理的是数组,需要像数组一样。

//scoped_array的模拟实现
template <class T>
class Scoped_array
{
public:
    Scoped_array(T* ptr)
        :_ptr(ptr)
    {}
    ~Scoped_array()
    {
        if (_ptr)
        {
            delete[] _ptr;
        }
    }
    T& opeartor[](size_t i)
    {
        return _ptr[i];
    }
private:
    Scoped_array(const Scoped_array<T>& sa);
    Scoped_array<T>& operator=(const Scoped_array& sa);

    T* _ptr;
};
int main()
{
    Scoped_array<int> sa(new int[3]);
    return 0;
}

详解shared_ptr

//shared_ptr模拟实现
template <class T>
class Shared_ptr
{
public:
    Shared_ptr(T* ptr == NULL)
        :_ptr(ptr)
        , _refCount(new int(1))
    {}
    Shared_ptr(const Shared_ptr<T>&sp)
        :_ptr(sp._ptr)
        ,_refCount(sp._refCount)
    {
        *_refCount++;
    }
    Shared_ptr<T>&operator=(const Shared_ptr<T>& sp)
    {
        if (_ptr != &sp._ptr)
        {
            if (--(*_refCount) == 0)
            {
                delete _ptr;
                delete _refCount;
            }
            _ptr = sp._ptr;
            _refCount = sp._refCount;
            (*_refCount)++;
        }
        return *this;
    }
    ~Shared_ptr()
    {
        if (--(*_refCount) == 0)
        {
            delete _ptr;
            delete _refCount;
        }
    }
    T& operator*()
    {
        return *_ptr;
    }
    T* opeartor->()
    {
        return _ptr;
    }
    int Count()
    {
        return *_refCount;
    }
private:
    T* _ptr;
    int *_refCount;
};

Shared_ptr会出现循环引用:

这里写图片描述

//测试代码
struct ListNode
{
    int _data;
    Shared_ptr<ListNode> _next;
    Shared_ptr<ListNode> _prev;
    ListNode(int x)
        :_data(x)
        , _next(NULL)
        , _prev(NULL)
    {}
    ~ListNode()
    {}
};
int main()
{
    Shared_ptr<ListNode> cur(new ListNode(1));
    Shared_ptr<ListNode> next(new ListNode(2));
    cur->_next = next;
    next->_prev = cur;

    cout << "cur._refCount  " <<cur.Count()<<endl;
    cout << "next._refCount  "<<cur.Count()<<endl;
    system("pause");
}

这里写图片描述

为什么每个节点都有两个引用计数呢?这就是循环引用的问题。
为了解决循环引用问题,这里需引入weak_ptr.

详解weak_ptr
weak_ptr是和shared_ptr配合使用的,weak_ptr不增加节点的引用计数。

//weak_ptr的模拟实现
template <class T>
class Weak_ptr
{
public:
    Weak_ptr()
        :_ptr(NULL)
    {}
    Weak_ptr(const Shared_ptr<T>& sp)
        :_ptr(sp._ptr)
    {}
    Weak_ptr<T>& operator=(const Shared_ptr<T>& sp)
    {
        _ptr = sp._ptr;
        return *this;
    }
    ~Weak_ptr()
    {
        if (_ptr)
        {
            delete _ptr;
        }
    }
private:
    T* _ptr;
};

那么weak_ptr是如何解决循环引用问题?

struct ListNode
{
    int _data;
    Weak_ptr<ListNode> _next;
    Weak_ptr<ListNode> _prev;
    ListNode(int x)
        :_data(x)
        , _next()
        , _prev()
    {}
    ~ListNode()
    {}
};
int main()
{
    Shared_ptr<ListNode> cur(new ListNode(3));
    Shared_ptr<ListNode> next(new ListNode(4));

    cur->_next = next;
    next->_prev = cur;

    cout << "cur._refCount  " << cur.Count() << endl;
    cout << "next._refCount  "<<cur.Count()<<endl;
    system("pause");
}

这里写图片描述

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值