C++智能指针

原创 2017年12月06日 15:59:11

智能指针:存储指向动态分配对象指针的类。
满足的条件:具有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名字没变。

详解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;
        }
    }
    T& operator*()
    {
        return *this;
    }
    T* operator->()
    {
        return _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");
}

这里写图片描述

版权声明:本文为博主原创文章,未经博主允许不得转载。

c++11智能指针解析——揭开底层面纱,完整理解智能指针

昨天跟同事小小的研究了下关于不同平台下,起因是遇到了一个坑,vs上没有问题,在安卓上却崩溃了。找了半天后发现是c++字节补齐问题,期间包括使用#pragma pack(1)来限定字节对齐方式等各种条件...
  • zy19940906
  • zy19940906
  • 2016年01月07日 17:00
  • 9163

浅谈C++普通指针和智能指针管理动态内存的陷阱

前言: C++中动态内存的管理主要是使用new/delete表达式和std::allcator类。 为了管理动态内存更加安全,C++11新标准库推出了智能指针。这里只讨论使用他们在使...
  • qq_33850438
  • qq_33850438
  • 2016年11月04日 13:51
  • 2491

C++面试题(四)——智能指针的原理和实现

普通C++面试时候的一般都是这个套路:      1,C++和C相比最大的特点——面向对象:封装,继承,多态。      2,你知道虚函数吗?——实现多态所必须,父类类型的指针指向子类的实例,执行的时...
  • worldwindjp
  • worldwindjp
  • 2014年01月28日 17:00
  • 11566

C++智能指针测试代码

  • 2016年04月15日 14:47
  • 5KB
  • 下载

c++智能指针的实现

  • 2015年07月02日 17:50
  • 715KB
  • 下载

C和C++使用Lu键树实现智能指针及检查内存泄露

  • 2014年01月05日 16:22
  • 185KB
  • 下载

C++ 智能指针

  • 2016年04月20日 22:57
  • 22KB
  • 下载

C++ 智能指针(shared_ptr/weak_ptr)源码

  • 2016年06月10日 19:46
  • 32KB
  • 下载

c++ 复制控制和智能指针

  • 2016年06月04日 20:48
  • 9KB
  • 下载

C++ 解环引用智能指针

  • 2014年06月03日 19:14
  • 5KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C++智能指针
举报原因:
原因补充:

(最多只允许输入30个字)