一 shared_ptr的循环引用问题
一:智能指针发展历史
智能指针发展历史:
struct ListNode
{
int _data;
ListNode* _next;
ListNode* _prev;
~ListNode()
{
cout << "~ListNode()" << endl;
}
};
void Test()
{
ListNode* cur = new ListNode;
ListNode* next = new ListNode;
cur->_next = next;//如果此处抛异常便不能释放 return throw
next->_prev = cur;
delete cur;
delete next;
}
以上代码,就是两个链表结点,连起来,然后释放。但是我们会发现如果再delete之前return,那就会出现内存泄漏,因为底下的delete根本没有机会执行。
于是我们就想到用智能指针来管理,(RAII)析构时自己释放(如果不懂,就去参考上篇智能指针的博客)
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<memory>
#include<boost/smart_ptr.hpp>
using namespace std;
struct ListNode
{
int _data;
std::shared_ptr<ListNode> _prev;
std::shared_ptr<ListNode> _next;
~ListNode()
{
cout << "~ListNode()" << endl;
}
};
void test_smart_ptr()
{
std::shared_ptr<ListNode>cur(new ListNode);//有内存泄漏
std::shared_ptr<ListNode>next(new ListNode);
//下面两句导致循环引用
//cur->_next = next;
//next->_prev = cur;
}
什么原因导致的呢?
循环引用,我们都知道,出了作用域,cur和next都被释放了,两个结点的引用计数都减为1,因为分别有智能指针_prev和智能指针_next指向这两个结点,但是你就会发下如果
想要把第一块空间释放,就必须把第二块空间_prev先释放,第二块空间想要释放,就要把第一块空间的_next释放,由于两块空间的释放都限制于对方,所以就都释放不了,造成内存泄漏。
三 如何解决循环引用问题(weak_ptr弱指针)
为了解决循环引用的问题我们加入了弱指针weak_ptr来辅助shared_ptr。注意weak_ptr不能单独使用,必须辅助shared_ptr才能使用。weak_ptr是一种不控制所指向对象生存周期的智能指针,它指向一个由shared_ptr管理的对象,将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数,一旦最后一个指向对象的shared_ptr被销毁,对象就会被销毁,即使有weak_ptr指向对象,对象还是会被释放。
因此上面代码可改为。
struct ListNode
{
int _data;
std::weak_ptr<ListNode> _prev;
std::weak_ptr<ListNode> _next;
~ListNode()
{
cout << "~ListNode()" << endl;
}
};
void test_smart_ptr()
{
std::shared_ptr<ListNode>cur(new ListNode);//有内存泄漏
std::shared_ptr<ListNode>next(new ListNode);
cout << cur.use_count ()<< endl;
cout << next.use_count() << endl;
//下面两句导致循环引用
cur->_next = next;
next->_prev = cur;
cout << cur.use_count() << endl;
cout << next.use_count() << endl;
}
看下weak_ptr的实现
template<class T>
class Weakptr;
template<class T>
class Sharedptr
{
friend class Weakptr<T>;//声明成友元,这样就可以在Weakptr中访问Sharedptr的私有成员
public:
Sharedptr(T* ptr = NULL)
:_ptr(ptr)
, _pcount(new int(1))
{}
~Sharedptr()
{
if (--*(_pcount) == 0)
{
delete _ptr;
delete _pcount;
_ptr = NULL;
_pcount = NULL;
}
}
Sharedptr(const Sharedptr<T> &sp)
{
_ptr = sp._ptr;
_pcount = sp._pcount;
++(*_pcount);
}
Sharedptr<T>& operator=(Sharedptr<T>& sp)
{
if (_ptr != sp._ptr)
{
if (--(*_pcount) == 0)
{
delete _ptr;
delete _pcount;
}
_ptr = sp._ptr;
_pcount = sp._pcount;
(*_pcount)++;
}
return *this;
}
int Use_count()
{
return *_pcount;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
int*_pcount;//不能为int count 原因是:如果为int count,那就说明每个对象有各自count,如果是指针,多个对象指向同一块空间
};
template<class T>
class Weakptr//注意Weakptr不能单独存在,她辅助Sharedptr
{
public:
Weakptr()
:_ptr(NULL)
{}
Weakptr(Sharedptr<T>& sp)
{
_ptr = sp._ptr;
}
Weakptr<T>& operator= (Sharedptr<T>& sp)
{
_ptr = sp._ptr;
return *this;
}
T& operator*()
{
return *_ptr;
}
private:
T* _ptr;
};
struct ListNode
{
int _data;
Weakptr<ListNode> _prev;//没有缺省构造的类成员变量,必须在初始化列表初始化。因此必须给个Sharedptr缺省构造
Weakptr<ListNode> _next;
~ListNode()
{
cout << "~ListNode()" << endl;
}
};
void testSharedptr()
{
Sharedptr<ListNode>cur(new ListNode);//有内存泄漏
Sharedptr<ListNode>next(new ListNode);
cout << cur.Use_count() << endl;
cout << next.Use_count() << endl;
//下面两句导致循环引用
cur->_next = next;
next->_prev = cur;
cout << cur.Use_count() << endl;
cout << next.Use_count() << endl;
}
int main()
{
testSharedptr();
system("pause");
return 0;
}
二 利用仿函数实现定制删除器
一 首先我们要了解什么是仿函数
仿函数:就是使一个类的使用看上去像一个函数,仿函数就是一个空类,里面重载了operator(),这样这个函数就有了类似函数的行为,就是一个仿函数类了。
class Less
{
public:
bool operator()(const int&l, const int &r)
{
return 1 < r;
}
};
int main()
{
int s1 = 2;
int s2 = 3;
Less less;
cout << less(s1, s2) << endl;//less.operator(s1,s2);
system("pause");
return 0;
}
二 简单的利用仿函数实现定制删除器
在这里我们需要对一个智能指针的问题探究,当我们进行资源处理时。无非是两种资源,一种是文件,一种是堆内存。对于文件我们fopen,然后fclose,对于堆内存,我们可能会new new[] malloc,当我们释放的时候,当然对应着delete,delete[],free。所以我们使用智能指针的时候,需要让它对删除器进行操作,比如我们打开一个文件能够让它识别,最后关闭文件。
int main()
{
std::shared_ptr<FILE>sp1(fopen("haha.txt", "w"));
}
如图上面的代码,智能指针默认的是管理内存new[],delete[],而上面是个文件资源因此报错。
class Fclose
{
public:
void operator()(FILE* f)
{
fclose(f);
cout << "FLCLOSE" << endl;
}
};
void test()
{
Fclose fc;
std::shared_ptr<File>sp1(fopen("haha.txt", "w"),fc);//将Fclose对象传递过去,文件正常关闭
}
int main()
{
test();
system("pause");
return 0;
}
再举一个例子,用智能指针malloc开辟的的动态内存,我们在释放的时候就要用free释放。
//定制删除器的仿函数
class Free
{
public:
void operator()(void* ptr)
{
free(ptr);
cout << "Free" << endl;
}
};
void test()
{
Free fr;
std::shared_ptr<int>sp1((int *)malloc(sizeof(int)),fr);//将Fclose对象传递过去,文件正常关闭
}
int main()
{
test();
system("pause");
return 0;
}