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");
}

这里写图片描述

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39295755/article/details/78714249

模板和智能指针(c++)

  • 2011年07月24日 22:55
  • 500KB
  • 下载

c++学习笔记—动态内存与智能指针浅析

我们的程序使用内存包含以下几种: 静态内存用来保存局部static对象、类static数据成员以及定义在任何函数之外的变量,在使用之前分配,在程序结束时销毁。 栈内存用来保存定义在函数内部的非st...
  • xujian_2014
  • xujian_2014
  • 2015-01-08 15:45:18
  • 811

C++几种智能指针之间的比较

这些智能指针在设计的时候,一个关键的问题就是所有权的控制。如果把指针所指向的对象比作电视机的话,那么指针就是观众。第一个人需要看电视的时候需要打开它,没人看的时候就要保证把电视关掉。 对于std...
  • nyist327
  • nyist327
  • 2015-05-21 23:59:41
  • 1680

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

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

C++智能指针与类继承多态

我在做编译器项目的时候, 我们采用c++语言,但要使用多态的性质,一是用引用,二是用指针。可是引用不够灵活,指针还具有内存管理问题。所以SmartPtr是一个必然的选择,可我发现通常的SmartPtr...
  • jiangzw625
  • jiangzw625
  • 2008-03-01 09:33:00
  • 2316

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

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

如何回答C++面试中关于智能指针的问题?

如何回答C++面试中关于智能指针的问题? 1、  什么是智能指针? 2、  分析下常见的智能指针有哪些? 3、实现一个智能指针呗?(没具体说写哪个,建议默认写:unique_ptr) 1、答:智能指针...
  • weizhengbo
  • weizhengbo
  • 2017-04-03 11:54:20
  • 1089

c++11 智能指针删除器

其实很简单自己上代码: #include #include #include #include #include using namespace std; struct struct...
  • u012592081
  • u012592081
  • 2016-07-22 18:24:54
  • 568

C++四种智能指针小结

C++四种智能指针auto_ptr、scope_ptr、shared_ptr和weak_ptr. 其中auto_ptr是C++98标准化引入的;scope_ptr、shared_ptr和weak_p...
  • e5Max
  • e5Max
  • 2016-01-23 12:33:31
  • 2939

C++11 智能指针

原作者:Babu_Abdulsalam 本文翻译自CodeProject,转载请注明出处。引入Ooops. 尽管有另外一篇文章说C++11里的智能指针了。近来,我听到许多人谈论C++新标准,就是所...
  • wizard_wsq
  • wizard_wsq
  • 2016-03-14 16:34:06
  • 558
收藏助手
不良信息举报
您举报文章:C++智能指针
举报原因:
原因补充:

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