一、智能指针:
1、智能指针给出的原因
2、智能指针原理
RAII:在构造函数中分配资源,在析构函数中释放资源,可以进行解引用或者通过->访问空间中的结构体变量。需要重载这几个符号。(迭代器也是把指针进行了封装、也进行了解引用、也进行了重载,所以迭代器也可以成为一种类型的智能指针)
3、了解哪些智能指针
迭代器也是把指针进行了封装、也进行了解引用、也进行了重载,所以迭代器也可以成为一种类型的智能指针
二、shared_ptr
1、ScopedPtr和sharedPtr在C++11中被加到了标准库中,原理相同。ScopedPtr是boost库里面的,boost库是c++中非常重要的一个库,和标准库一样提供了一些非常重要的功能供大家调用,用到时候也是免费的。(当你在不是很了解boost库里面详细的内容的时候,不要轻易说你了解ScopedPtr,因为你不了解其详细内容)在标准库中unique_ptr和其具有相同的功能,在unique_ptr管理资源只能管理一块资源,只能被一个智能指针的资源进行管理,不能几个指针进行共享。其两种实现方式在上一篇关于智能指针的博客中有讲到.
template<class T>
class SharedPtr
{
public:
//构造函数
SharedPtr(T* ptr = NULL)//没有传任何实参时,缺省值是空的
:_ptr(ptr)//赋值给当前对象
, _pCount(NULL)
{
if (_ptr)//如果不为空(即外部实参已经将资源给出来了)
{
_pCount = new int(1);//新申请一端空间,将空间中的内容给成1
//给成1是因为外部用户将资源给进来之后,当前只有一个对象进行管理
}
}
//拷贝构造
SharedPtr(const SharedPtr<T>& sp)
:_ptr(sp._ptr)//共用同一块空间
, _pCount(sp._pCount)//共用同一块引用计数
{
//智能给当前对象的引用计数加1
//当前对象如果存在,给引用计数++
if (_pCount)
{
++(*_pCount);
}
//_pCount && ++(*_pCount);//不使用循环和if判断实现上述方式
}
//赋值运算符的重载:sp1=sp2
//可能会出现的情况:a、sp1为NULL;b、sp1管理了一段空间,但是这段空间
//只是sp1一个对象在进行管理(资源独占)[应释放sp1的空间,
//在和sp2共用同一块空间,引用计数++】;
//c、sp1资源共享,【应将引用计数--】
SharedPtr<T>& operator=(const SharedPtr<T>& sp)
{
if (this!=&sp)
{
//当前对象管理资源了,_pCount一定不为空
//0==--(*_pCount说明当前对象自己独立管理资源
if (_pCount&&0==--(*_pCount)
{
delete _ptr;
delete _pCount;
}
//当前对象在里面没有管理资源,但是也会共享同一块引用计数
_ptr = sp._ptr;
_pCount = sp._pCount;
if (_pCount)
++(*_pCount);//共享同一块空间需要将引用计数++
}
return *this;
}
//析构函数
~SharedPtr()
{
if (_pCount && 0 == --(*_pCount))//当前对象独立管理资源
{
delete _ptr;
delete _pCount;
}
}
T& operator*()
{
return (*_ptr);
}
T& operator->()
{
return _ptr;
}
//
int UseCount()const
{
return (*_pCount);
}
private:
T* _ptr;
int *_pCount;//计数空间
};
void TestShared()
//sp1未管理资源
{
SharedPtr<int> sp1;
SharedPtr<int> sp2(new int);
cout << sp2.UseCount() << endl;
sp1 = sp2;
cout << sp2.UseCount() << endl;
}
void TestShared1()
//测试独立管理资源
{
SharedPtr<int> sp1(new int);
SharedPtr<int> sp2(new int);
cout << sp2.UseCount() << endl;
sp1 = sp2;
cout << sp2.UseCount() << endl;
}
void TestShared2()
//sp1共享资源
{
SharedPtr<int> sp1(new int);
SharedPtr<int> sp2(new int);
cout << sp2.UseCount() << endl;
SharedPtr<int> sp3(sp1);//sp1和sp3共享空间
sp1 = sp2;
cout << sp2.UseCount() << endl;
}
int main()
{
TestShared();
return 0;
}
拷贝构造函数的思路:
存在问题,若是使用如下测试代码,程序会出错:因为析构函数里面直接对函数进行了delete操作,而此时我们创建新空间使用的是malloc
void TestShared3()
{
SharedPtr<int> sp1((int*)malloc(sizeof(int)));
SharedPtr<FILE> sp2(fopen("1.txt","rb"));//rb按照只读二进制的形式打开
SharedPtr<int> sp3(new int);//sp1和sp3共享空间
}
不同的指针应该有不同的方式去处理指针的清理工作,
解决方式1:定制一个删除器:
template<class T>
void DeletePtr(T*& ptr)
{
if (ptr)
{
delete ptr;
ptr = NULL;
}
}
//对于malloc申请的空间
template<class T>
void FreePtr(T*& ptr)
{
if (ptr)
{
free(ptr);
ptr = NULL;
}
}
//文件指针
void FClosedPtr(FILE*& ptr)
{
if (ptr)
{
fclose(ptr);
ptr = NULL;
}
}
//给出函数指针
template<class T>
class _PDF
{
public:
typedef void(*PDF)(T*&);//定义一个函数指针的类型
};
template<class T>
class SharedPtr
{
typename typedef __PDF<T>::PDF PDF;
//typename相当于告诉编译器PDF是一个类型,不是静态成员变量
public:
//构造函数
SharedPtr(T* ptr = NULL,PDF pdf=DeletePtr)//没有传任何实参时,缺省值是空的
:_ptr(ptr)//赋值给当前对象
, _pCount(NULL)
,_pDf(pdf)
{
if (_ptr)//如果不为空(即外部实参已经将资源给出来了)
{
_pCount = new int(1);//新申请一端空间,将空间中的内容给成1
//给成1是因为外部用户将资源给进来之后,当前只有一个对象进行管理
}
}
//拷贝构造
SharedPtr(const SharedPtr<T>& sp)
:_ptr(sp._ptr)//共用同一块空间
, _pCount(sp._pCount)//共用同一块引用计数
{
//智能给当前对象的引用计数加1
//当前对象如果存在,给引用计数++
if (_pCount)
{
++(*_pCount);
}
//_pCount && ++(*_pCount);//不使用循环和if判断实现上述方式
}
//赋值运算符的重载:sp1=sp2
//可能会出现的情况:a、sp1为NULL;b、sp1管理了一段空间,但是这段空间
//只是sp1一个对象在进行管理(资源独占)[应释放sp1的空间,
//在和sp2共用同一块空间,引用计数++】;
//c、sp1资源共享,【应将引用计数--】
SharedPtr<T>& operator=(const SharedPtr<T>& sp)
{
if (this!=&sp)
{
//当前对象管理资源了,_pCount一定不为空
//0==--(*_pCount说明当前对象自己独立管理资源
if (_pCount&&0==--(*_pCount)
{
//delete _ptr;
_pDf(_ptr);
delete _pCount;
}
//当前对象在里面没有管理资源,但是也会共享同一块引用计数
_ptr = sp._ptr;
_pCount = sp._pCount;
if (_pCount)
++(*_pCount);//共享同一块空间需要将引用计数++
}
return *this;
}
//析构函数
~SharedPtr()
{
if (_pCount && 0 == --(*_pCount))//当前对象独立管理资源
{
//delete _ptr;
_pDf(_ptr);
delete _pCount;
}
}
T& operator*()
{
return (*_ptr);
}
T& operator->()
{
return _ptr;
}
//
int UseCount()const
{
return (*_pCount);
}
private:
T* _ptr;
int *_pCount;//计数空间
PDF _pDF;
};
void TestShared()
//sp1未管理资源
{
SharedPtr<int> sp1;
SharedPtr<int> sp2(new int);
cout << sp2.UseCount() << endl;
sp1 = sp2;
cout << sp2.UseCount() << endl;
}
void TestShared1()
//测试独立管理资源
{
SharedPtr<int> sp1(new int);
SharedPtr<int> sp2(new int);
cout << sp2.UseCount() << endl;
sp1 = sp2;
cout << sp2.UseCount() << endl;
}
void TestShared2()
//sp1共享资源
{
SharedPtr<int> sp1(new int);
SharedPtr<int> sp2(new int);
cout << sp2.UseCount() << endl;
SharedPtr<int> sp3(sp1);//sp1和sp3共享空间
sp1 = sp2;
cout << sp2.UseCount() << endl;
}
void TestShared3()
{
SharedPtr<int> sp1((int*)malloc(sizeof(int)),FreePtr);
SharedPtr<int> sp2(fopen("1.txt","rb"),FClosedPtr);//rb按照只读二进制的形式打开
SharedPtr<int> sp3(new int);//sp1和sp3共享空间
}
int main()
{
TestShared3();
return 0;
}
解决方式2:加上一个模板类型的参数
//函数参数
//相当于把Dx创建的无名对象像函数一样使用(即仿函数):
//只需要在类里面重载()
template<class T>
class Delete
{
public:
//重载()
void operator()(T*& ptr)
{
if (ptr)
{
delete ptr;
ptr = NULL;
}
}
};
template<class T>
class Free
{
public:
void operator()(T*& ptr)
{
if (ptr)
{
free(ptr);
ptr=NULL:
}
}
};
//文件指针的类,本身就是FILE*,不需要给成模板类
class FClose
{
public:
void operator()(FILE*& ptr)
{
if (ptr){
fclose(ptr);
ptr = NULL;
}
}
};
template<class T,class Dx=Delete<T>>//此时Dx是一个类型
class SharedPtr
{
public:
//构造函数
SharedPtr(T* ptr = NULL)//没有传任何实参时,缺省值是空的
:_ptr(ptr)//赋值给当前对象
, _pCount(NULL)
{
if (_ptr)//如果不为空(即外部实参已经将资源给出来了)
{
_pCount = new int(1);//新申请一端空间,将空间中的内容给成1
//给成1是因为外部用户将资源给进来之后,当前只有一个对象进行管理
}
}
//拷贝构造
SharedPtr(const SharedPtr<T>& sp)
:_ptr(sp._ptr)//共用同一块空间
, _pCount(sp._pCount)//共用同一块引用计数
{
//智能给当前对象的引用计数加1
//当前对象如果存在,给引用计数++
if (_pCount)
{
++(*_pCount);
}
//_pCount && ++(*_pCount);//不使用循环和if判断实现上述方式
}
//赋值运算符的重载:sp1=sp2
//可能会出现的情况:a、sp1为NULL;b、sp1管理了一段空间,但是这段空间
//只是sp1一个对象在进行管理(资源独占)[应释放sp1的空间,
//在和sp2共用同一块空间,引用计数++】;
//c、sp1资源共享,【应将引用计数--】
SharedPtr<T>& operator=(const SharedPtr<T>& sp)
{
if (this!=&sp)
{
//当前对象管理资源了,_pCount一定不为空
//0==--(*_pCount说明当前对象自己独立管理资源
if (_pCount&&0==--(*_pCount)
{
Dx()(_ptr);//直接加上()相当于创建了一个对象
//类似于一个函数对象
delete _pCount;
}
//当前对象在里面没有管理资源,但是也会共享同一块引用计数
_ptr = sp._ptr;
_pCount = sp._pCount;
if (_pCount)
++(*_pCount);//共享同一块空间需要将引用计数++
}
return *this;
}
//析构函数
~SharedPtr()
{
if (_pCount && 0 == --(*_pCount))//当前对象独立管理资源
{
Dx()(_ptr);//直接加上()相当于创建了一个对象
//类似于一个函数对象
delete _pCount;
}
}
T& operator*()
{
return (*_ptr);
}
T& operator->()
{
return _ptr;
}
//
int UseCount()const
{
return (*_pCount);
}
private:
T* _ptr;
int *_pCount;//计数空间
};
void TestShared()
{
SharedPtr<int,Free<int>> sp1((int*)malloc(sizeof(int)));
SharedPtr<FILE,FClose> sp2(fopen("1.txt", "rb"));//rb按照只读二进制的形式打开
SharedPtr<int> sp3(new int);//sp1和sp3共享空间
}
int main()
{
TestShared();
return 0;
}
3、使用标准库中的shared_ptr处理问题:
int main()
{
shared_ptr<int> sp1(new int);//管理单个的空间
cout << sp1.use_count() << endl;
//sp1拷贝构造sp2
shared_ptr<int> sp2(sp1);
cout << sp1.use_count() << endl;
return 0;
}
4、shared_ptr自身有一个很大的缺陷,有可能存在循环引用的问题,没有调用析构函数,会发生内存泄漏,如下述程序:
# include"memory"
template<class T>
struct ListNode
{
//构造函数
ListNode(const T& data)
:_pPre(NULL)
, _pNext(NULL)
, _data(data)
{
cout << "ListNode(const T&):" << this << endl;
}
~ListNode()
{
cout << "~ListNode():" << this<<endl;
}
//ListNode<T>* _pPre;
//ListNode<T>* _pNext;
shared_ptr<ListNode<T>> _pPre;
shared_ptr < ListNode<T>> _pNext;
T _data;
};
void TestSharedPtr()
{
//ListNode<int>* p=new ListNode<T> (10);
shared_ptr<ListNode<int>> sp1(new ListNode<int> (10));
shared_ptr<ListNode<int>> sp2(new ListNode<int>(20));
cout << sp1.use_count() << endl;
cout << sp2.use_count() << endl;
//将两个节点链接起来
sp1->_pNext = sp2;
sp2->_pPre = sp1;
cout << sp1.use_count() << endl;
cout << sp2.use_count() << endl;
}
int main()
{
TestSharedPtr();
system("pause");
return 0;
}
结果为2,没有进行析构、释放
5、解决循环引用的问题:weak_ptr,weak_ptr不能独立的管理空间。和shared_ptr一起使用,解决循环引用的问题,此时会调用析构函数【循环引用的场景,面试重要程度:5颗星】
# include"memory"
template<class T>
struct ListNode
{
//构造函数
ListNode(const T& data)
:_data(data)
{
cout << "ListNode(const T&):" << this << endl;
}
~ListNode()
{
cout << "~ListNode():" << this<<endl;
}
//ListNode<T>* _pPre;
//ListNode<T>* _pNext;
weak_ptr<ListNode<T>> _pPre;
weak_ptr < ListNode<T>> _pNext;
T _data;
};
void TestSharedPtr()
{
//weak_ptr<int> wp(new int);//不能单独使用,必须依附在shared_ptr之上
//ListNode<int>* p=new ListNode<T> (10);
shared_ptr<ListNode<int>> sp1(new ListNode<int> (10));
shared_ptr<ListNode<int>> sp2(new ListNode<int>(20));
cout << sp1.use_count() << endl;
cout << sp2.use_count() << endl;
//将两个节点链接起来
sp1->_pNext = sp2;
sp2->_pPre = sp1;
cout << sp1.use_count() << endl;
cout << sp2.use_count() << endl;
}
int main()
{
TestSharedPtr();
system("pause");
return 0;
}
结果: