智能指针11
Posted 蚍蜉撼树谈何易
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了智能指针11相关的知识,希望对你有一定的参考价值。
智能指针
智能指针存在的必要性
- malloc出来的空间,没有进行释放,存在内存泄漏的问题。
- 异常安全问题。如果在malloc和free之间如果存在抛异常,那么还是有内存 泄漏。这种问题就叫异常安全
智能指针的使用及原理
RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:
不需要显式地释放资源。
采用这种方式,对象所需的资源在其生命期内始终保持有效。
原理:
1. RAII特性
2. 重载operator*和opertaor->,具有像指针一样的行为
3. 解决浅拷贝的问题
C++98中的智能指针
auto_ptr:
edition1:
如何解决浅拷贝问题:资源转移。
缺点:转移资源,若此时想对原对象操作会出错。
namespace bit
{
template<class T>
class auto_ptr
{
public:
auto_ptr(T* ptr=nullptr)
:_ptr(ptr)
{}
auto_ptr(auto_ptr<T>& pt):_ptr(pt._ptr)
{
pt._ptr = nullptr;
}
auto_ptr<T>& operator=(auto_ptr<T>& ap)
{
if (this != &ap)
{
// 此处需要将ap中的资源转移给this
// 但是不能直接转移,因为this可能已经管理资源了,否则就会造成资源泄漏
if (_ptr)
{
delete _ptr;
}
// ap就可以将其资源转移给this
_ptr = ap._ptr;
ap._ptr = nullptr; // 让ap与之前管理的资源断开联系,因为ap中的资源已经转移给this了
}
return *this;
}
~auto_ptr()
{
if (_ptr)
{
delete _ptr;
_ptr = nullptr;
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
T* Get()
{
return _ptr;
}
private:
T* _ptr;
};
};
editio2:转移释放权限
namespace bit
{
template<class T>
class auto_ptr
{
public:
auto_ptr(T* ptr =nullptr)
:_ptr(ptr)
,owner(false)
{
if (_ptr)
{
owner = true;
}
}
auto_ptr(auto_ptr<T>& t)
:_ptr(t._ptr)
,owner(t.owner)
{
t.owner = false;
}
auto_ptr<T>& operator=(auto_ptr<T>& t)
{
if (this != &t)
{
if (this->_ptr && this->owner)
{
delete _ptr;
}
_ptr = t._ptr;
owner = t.owner;
t.owner = false;
}
return *this;
}
~auto_ptr()
{
if (_ptr && owner)
{
delete _ptr;
owner = false;
_ptr = nullptr;
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
T* Get()
{
return _ptr;
}
private:
T* _ptr;
bool owner;
};
}
void TestAutoPtr()
{
bit::auto_ptr<int> ap1(new int);
*ap1 = 100;
bit::auto_ptr<int> ap2(ap1);
// 和我们对指针常规的认知有区别的
int* p1 = new int;
int* p2(p1);
*p1 = 10;
*p2 = 20;
delete p1;
p1 = p2 = nullptr;
// auto_ptr采用资源转移的方式虽然将浅拷贝的问题解决了,但是引用了新的问题
if (ap2.Get())
*ap2 = 2000;
if (ap1.Get())
*ap1 = 1000; // 代码会崩溃,因为ap1当中的资源已经转移走了,ap1当中的指针指向的空
bit::auto_ptr<int> ap3(new int);
*ap3 = 300;
bit::auto_ptr<int> ap4(new int);
*ap4 = 400;
ap3 = ap4;
//
// 致命的缺陷---可以会导致野指针
if (true)
{
bit::auto_ptr<int> ap5(ap2);
*ap5 = 100;
*ap2 = 200;
*ap1 = 300;
// 再来开if的作用域时,ap5已经将管理的资源释放掉了
// 而ap1和ap2根本就不知道,其内部的指针称为野指针了
}
// 如果通过ap1和ap2再访问资源时,代码就会出问题
*ap2 = 10;
}
int main()
{
TestAutoPtr();
return 0;
}
缺陷:可能会导致野指针的情况,比如上面的示例
所以:建议不要使用auto_ptr
C++11中的智能指针
C++11中unique_ptr指针
这个智能指针的作用就是防拷贝,既然拷贝可能会带来资源重复释放,资源泄露的问题,那我从根源上就不让你拷贝。
// unique_ptr: RAII + operator*()/operator->()+解决浅拷贝方式:禁止拷贝---资源独占
// 一份资源只能被一个对象来进行管理,对象之间不能共享资源
// 应用场景:只能应用与资源被一个对象管理 并且不会被共享的场景当中
// 缺陷:多个对象之间不能共享资源
// 负责释放new资源
template<class T>
class DFDef
{
public:
void operator()(T*& ptr)
{
if (ptr)
{
delete ptr;
ptr = nullptr;
}
}
};
// 负责:malloc的资源的释放
template<class T>
class Free
{
public:
void operator()(T*& ptr)
{
if (ptr)
{
free(ptr);
ptr = nullptr;
}
}
};
// 关闭文件指针
class FClose
{
public:
void operator()(FILE*& ptr)
{
if (ptr)
{
fclose(ptr);
ptr = nullptr;
}
}
};
namespace bite
{
// T: 资源中所放数据的类型
// DF: 资源的释放方式
// 定制删除器
template<class T, class DF = DFDef<T>>
class unique_ptr
{
public:
/
// RAII
unique_ptr(T* ptr = nullptr)
: _ptr(ptr)
{}
~unique_ptr()
{
if (_ptr)
{
// 问题:_ptr管理的资源:可能是从堆上申请的内存空间、文件指针、malloc空间...
// delete _ptr; // 注意:此处的释放资源的方式不能写死了,应该按照资源类型不同找对应的方式释放
// malloc--->free
// new---->delete
// fopen--->fclose关闭
DF df;
df(_ptr);
}
}
// 具有指针类似的行为
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
T* Get()
{
return _ptr;
}
// 解决浅拷贝方式---资源独占,即防止被拷贝
// C++98
/*
将拷贝构造函数以及赋值运算符重载方法只声明不定义,并且需要将其权限设置为私有的
// 只声明不定义,没有将权限设置为private---不行的:因为方法别人可以再类外定义
*/
private:
unique_ptr(const unique_ptr<T, DF>&);
unique_ptr<T, DF>& operator=(const unique_ptr<T, DF>&);
// C++11: 可以让编译器不生成默认的拷贝构造以及赋值运算符重载---delete
// 在C++11当中,delete关键字的功能扩展:释放new申请的空间 用其修饰默认成员函数,表明:编译器不会生成了
// unique_ptr(const unique_ptr<T,DF>&) = delete; // 表明:编译器不会生成默认的拷贝构造函数
// unique_ptr<T,DF>& operator=(const unique_ptr<T,DF>&) = delete;// 表明:编译器不会生成默认的赋值运算符重载
private:
T* _ptr;
};
// 用户在外部可以对方法进行定义---在unique_ptr的类中如果将该权限设置为private的
//template<class T>
//unique_ptr<T>::unique_ptr(const unique_ptr<T>& up)
//{}
}
#include <memory>
void TestUniquePtr()
{
bite::unique_ptr<int> up1(new int);
bite::unique_ptr<int, Free<int>> up2((int*)malloc(sizeof(int)));
bite::unique_ptr<FILE, FClose> up3(fopen("12345.txt", "w"));
}
int main()
{
TestUniquePtr();
bite::unique_ptr<int> up1(new int);
// bite::unique_ptr<int> up2(up1);
bite::unique_ptr<int> up3(new int);
// up1 = up3;
///
// 标准库
unique_ptr<int> up4(new int);
// unique_ptr<int> up5(up4);
unique_ptr<int> up6(new int);
// up4 = up6;
return 0;
}
C’++11中的shared_ptr
// 负责释放new资源
template<class T>
class DFDef
{
public:
void operator()(T*& ptr)
{
if (ptr)
{
delete ptr;
ptr = nullptr;
}
}
};
// 负责:malloc的资源的释放
template<class T>
class Free
{
public:
void operator()(T*& ptr)
{
if (ptr)
{
free(ptr);
ptr = nullptr;
}
}
};
// 关闭文件指针
class FClose
{
public:
void operator()(FILE*& ptr)
{
if (ptr)
{
fclose(ptr);
ptr = nullptr;
}
}
};
#include <mutex>
namespace bite
{
// shared_ptr: 自身才是安全的---加锁:为了保证shared_ptr自身的安全性
template<class T, class DF = DFDef<T>>
class shared_ptr
{
public:
//
// RAII
shared_ptr(T* ptr = nullptr)
: _ptr(ptr)
, _pCount(nullptr)
, _pMutex(new mutex)
{
if (_ptr)
{
// 此时只有当前刚刚创建好的1个对象在使用该份资源
_pCount = new int(1);
}
}
~shared_ptr()
{
Release();
}
/
// 具有指针类似的行为
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
T* Get()
{
return _ptr;
}
//
// 用户可能需要获取引用计数
int use_count()const
{
return *_pCount;
}
///
// 解决浅拷贝方式:采用引用计数
shared_ptr(const shared_ptr<T>& sp)
: _ptr(sp._ptr)
, _pCount(sp._pCount)
, _pMutex(sp._pMutex)
{
AddRef();
}
shared_ptr<T, DF>& operator=(const shared_ptr<T, DF>& sp)
{
if (this != &sp)
{
// 在和sp共享之前,this先要将之前的状态清空
Release();
// this就可以和sp共享资源以及计数了
_ptr = sp._ptr;
_pCount = sp._pCount;
_pMutex = sp._pMutex;
AddRef();
}
return *this;
}
private:
void AddRef()
{
if (nullptr == _ptr)
return;
_pMutex->lock();
++(*_pCount);
_pMutex->unlock();
}
void Release()
{
if (nullptr == _ptr)
return;
bool isDelete = false;
_pMutex->lock();
if (_ptr && 0 == --(*_pCount))
{
// delete _ptr;
DF df;
df(_ptr);
delete _pCount;
_pCount = nullptr;
isDelete = true;
}
_pMutex->unlock();
if (isDelete)
{
delete _pMutex;
}
}
private:
T* _ptr; // 用来接收资源的
int* _pCount; // 指向了引用计数的空间---记录的是使用资源的对象的个数
mutex* _pMutex; // 目的:保证对引用计数的操作是安全的
};
}
void TestSharedPtr()
{
bite::shared_ptr<int> sp1(new int);
bite::shared_ptr<int> sp2(sp1);
bite::shared_ptr<int> sp3(new int);
bite::shared_ptr<int> sp4(sp3);
sp3 = sp2; // sp2和sp3共享同一份资源了
sp4 = sp2; // sp2和sp4共享同一份资源了
}
// 这个语法也是C++11新的语法:在定义成员变量时,可以就地初始化
struct A
{
int a = 0;
int b = 0;
int c = 0;
};
// 线程函数
void ThreadFunc(bite::shared_ptr<A>& sp, int n)
{
for (int i = 0; i < n; ++i)
{
bite::shared_ptr<A> copy(sp);
copy->a++;
copy->b++;
copy->c++;
}
}
#include <thread>
void TestSharedPtrSafe()
{
bite::shared_ptr<A> sp(new A);
// t1对象将来就和对应的线程绑定一起了
thread t1(ThreadFunc, sp, 10000);
thread t2(ThreadFunc, sp, 10000);
// 线程等待
t1.join();
t2.join();
// 1. 代码会不会崩溃----->shared_ptr是否安全---没有崩溃:
// 2. A 对象中的各个成员的值是不是都是20000
cout << sp->a << endl;
cout << sp->b << endl;
cout << sp->c << endl;
}
/
// 利用标准库中的shared_ptr来进行测试
#include <memory>
void ThreadFuncstd(shared_ptr<A>& sp, int n)
{
for (以上是关于智能指针11的主要内容,如果未能解决你的问题,请参考以下文章