智能指针
1. 为什么需要智能指针?
1.malloc出来的空间,没有进行释放,存在内存泄漏的问题。
2.同样的,new 出来的空间,没有进行释放,也会存在内存泄漏问题。
2. 智能指针使用即原理
1.原理: 是一种利用对象生命周期来控制程序资源的技术。
在对象构造时获取资源,在对象析构时销毁资源。
实际上把管理一份资源托付给了对象:
- 不需要显示释放资源。
- 采用这种方式,对象所需的资源在其生命周期内始终有效。
常见的几种智能指针
1.C++98 auto_ptr 原理:资源转移
namespace bite
{
template<class T>
class auto_ptr
{
public:
auto_ptr(T* ptr)
:_ptr(ptr)
{}
auto_ptr(auto_ptr<T>& ap)
:_ptr(ap._ptr)
{
ap._ptr = nullptr;
}
auto_ptr<T>& operator=(auto_ptr<T>& ap)
{
if (this != &ap)
{
if (_ptr)
delete _ptr;
_ptr = ap._ptr;
ap._ptr = nullptr;
}
return *this;
}
~auto_ptr()
{
if (_ptr)
delete _ptr;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
}
void TestFunc()
{
bite::auto_ptr<int> ap1(new int);
bite::auto_ptr<int> ap2(ap1);
*ap2 = 20;
bite::auto_ptr<int> ap3(new int);
ap3 = ap2;
}
int main()
{
TestFunc();
return 0;
}
缺点:调用拷贝构造后,ap1为nullptr, 通过ap1访问资源就会出错。
改进:
添加对资源的释放权力.
namespace bite
{
template<class T>
class auto_ptr
{
public:
auto_ptr(T* ptr = nullptr)
:_ptr(ptr)
,_owner(false)
{
if (_ptr)
_owner = true;
}
auto_ptr(const auto_ptr<T>& ap)
:_ptr(ap._ptr)
,_owner(ap._owner)
{
ap._owner = false;
}
auto_ptr<T>& operator=(const auto_ptr<T>& ap)
{
if (this != &ap)
{
if (_ptr && _owner)
delete _ptr;
_ptr = ap._ptr;
_owner = ap._owner;
ap._owner = false;
}
return *this;
}
~auto_ptr()
{
if (_ptr && _owner)
delete _ptr;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
mutable bool _owner;
};
}
void TestFunc1()
{
bite::auto_ptr<int> ap1(new int);
bite::auto_ptr<int> ap2(ap1);
bite::auto_ptr<int> ap3;
ap3 = ap1;
*ap1 = 10;
*ap2 = 20;
*ap3 = 30;
}
void TestFunc2()
{
//容易形成野指针
bite::auto_ptr<int> ap1(new int);
bite::auto_ptr<int> ap2(ap1);
if (true)
{
bite::auto_ptr<int> ap3(ap2);//出作用域,销毁ap3
}
}
int main()
{
TestFunc2();
return 0;
}
缺点:容易形成野指针。
- 在C++11中 C++爱好者更新了几个版本的智能指针
- unique_ptr (不要拷贝构造函数和赋值运算符)
#include<iostream>
using namespace std;
// 智能指针原理:ARII + 具有指针类似行为 + 解决浅拷贝
// unique_ptr: 资源独占的方式(一份资源只能被一个对象管理)
// 方式:禁止拷贝构造和赋值运算符重载调用---防拷贝
namespace bite
{
template<class T>
class unique_ptr
{
public:
unique_ptr(T* ptr = nullptr)
:_ptr(ptr)
{}
~unique_ptr()
{
if (_ptr)
delete _ptr;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
/*
private:
{//防止拷贝构造和赋值运算符的使用
unique_ptr(const unique_ptr<T>& up);
unique_ptr<T>& operator=(const unique_ptr<T>& up);
*/
unique_ptr(const unique_ptr<T>& up) = delete;
unique_ptr<T>& operator=(const unique_ptr<T>& up) = delete;
protected:
T* _ptr;
};
}// 缺陷:多个unique_ptr的对象之间不能共享资源
void TestFunc()
{
bite::unique_ptr<int> up1(new int);
//bite::unique_ptr<int> up2(up1);
bite::unique_ptr<int> up3;
//up2 = up1;
}
int main()
{
TestFunc();
return 0;
}
缺陷:多个unique_ptr的对象之间不能共享资源
- shared_ptr
解决浅拷贝问题:1.计数方法:(静态成员变量)[在创建新的变量时,count 会变为1,所以采用静态不可以]
namespace bite
{
template<class T>
class shared_ptr
{
public:
shared_ptr(T* ptr = nullptr)
:_ptr(ptr)
, _pCount(nullptr)
{
if (_ptr)
_pCount = new int(1);
}
shared_ptr(const shared_ptr<T>& sp)
:_ptr(sp._ptr)
,_pCount(sp._pCount)
{
if (_pCount)
++* _pCount;
}
shared_ptr<T>& operator=(const shared_ptr<T>& sp)
{
if (this != &sp)
{
Release();
_ptr = sp._ptr;
_pCount = sp._pCount;
if (_pCount)
++* _pCount;
}
return *this;
}
~shared_ptr()
{
Release();
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
int use_count()
{
return *_pCount;
}
private:
void Release()
{
if (_ptr && 0 == *_pCount)
{
delete _ptr;
delete _pCount;
}
}
protected:
T* _ptr;
int* _pCount;
};
}
void Testsharedptr()
{
bite::shared_ptr<int> sp1(new int);
bite::shared_ptr<int> sp2(sp1);
cout << sp1.use_count() << endl;
cout << sp2.use_count() << endl;
bite::shared_ptr<int> sp3(new int);
bite::shared_ptr<int> sp4(sp3);
cout << sp3.use_count() << endl;
cout << sp4.use_count() << endl;
sp3 = sp1;
cout << sp1.use_count() << endl;
sp4 = sp1;
cout << sp1.use_count() << endl;
}
int main()
{
Testsharedptr();
return 0;
}
因为需要访问临界资源问题。所以加锁才是安全的。
同时,shared_ptr 存在循环引用问题。
shared_ptr sp1(new ListNode(10));
shared_ptr sp2(new ListNode(20));
ListNode 是一个双向循环链表。
因为每个节点都是shared_ptr类型,所以next 和pre 都含有_ptr _Count 参数,所以当节点指向建立后,每个计数也需要相加,当sp1 不用,需要释放时,计数–,count变为1,同样,当sp2 不用,需要释放时,计数–,count变为1。但资源还没有被释放,想要释放sp1资源,就必须将sp1的count 变为0, 想要释放sp2资源,就必须将sp2的count 变为0.sp1的count为0,就必须将sp2中的pre指向改变,想要改变指向,就必须释放sp2,同样的,想要释放sp2,就要改变sp2的count,要想释放sp1。都想先销毁对方,才能释放。所以资源得不到释放。
解决方法:只需将shared_ptr换位 weak_ptr;
但是weak_ptr不能管理资源,所以pre(nullptr)和next(nullptr)
如果这样初始化就会报错。
struct ListNode {
ListNode(int data = int())
:_data(data)
// ,pre(nullptr)
//,next(nullptr)
{}
int _data;
// shared_ptr<ListNode> pre;
// shared_ptr<ListNode> next;
weak_ptr<ListNode> pre;
weak_ptr<ListNode> next;
};