您的位置:首页 > 编程语言 > C语言/C++

C++智能指针

2017-12-06 15:59 155 查看
智能指针:存储指向动态分配对象指针的类。

满足的条件:具有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;
4000
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");
}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息