1.什么是RAII
RAII是一种利用对象生命周期来控制程序资源(如内存,文件句柄,网络连接,互斥量等等)的简单技术。在对象构造时获取资源,接着控制对象的访问是指在对象的生命周期内始终保持有效,最后在对象析构时释放资源,借此,我们实际上是把管理一份资源的责任托管给了一个对象,这种做法有两大好处:
- 不需要显式的释放资源
- 采用这种方式,对象所需的资源在其生命期呃逆始终保持有效
2.智能指针
2.1什么是智能指针?
所谓的智能指针就是智能/自动化的管理指针所指向的动态资源的释放,智能指针是一个类,有类似指针的功能
2.2为什么需要智能指针?
c++程序设计中使用堆内存是非常频繁的操作,堆内存的申请与释放都是有程序员自己管理的,程序员自己管理堆内存可以提高程序的效率,但是整体来说堆内存的管理是非常麻烦的,c++11中引入了智能指针的概念,方便管理堆内存。使用普通指针,容易造成堆内存泄漏(忘记释放)二次释放,程序发生异常时内存泄漏等问题,使用智能指针能更好地管理堆内存。因为智能指针就是一个类,当超过类的作用域时,类会自动调用析构函数,析构函数会自动释放资源。
2.3.智能指针实现原理
实现原理:通过RAII方式将原生态管理起来–构造函数中放资源,析 构函数中将资源释放掉-----用户不需要关心什么时机释放资源,让该类对象具有像指针一样的行为:T&operstor*() T*operator->()
缺陷:具有浅拷贝问题
template<class T>
class smartptr {
public:
smartptr(T*ptr=nullptr)
:_ptr(ptr)
{}
~smartptr() {
if (_ptr) {
delete _ptr;
}
}
T& operator*() {
return *_ptr;
}
T* operator->() {
return _ptr;
}
private:
T* _ptr;
};
struct A {
int a;
int b;
int c;
};
void TestFunc() {
smartptr<int>sp1(new int);
*sp1 = 1;
smartptr<A>sp2(new A);
sp2->a = 2;
sp2->b = 3;
sp2->c = 4;
}
int main() {
TestFunc();
return 0;
}
不同类型的智能指针按照上述方式实现后,只需要解决浅拷贝的问题即可
c++98:auto_ptr–>RAII+operator*()/operator->()+资源管理权转移 auto_ptrap1(new int)
auto_ptrap2(ap1)
原理:管理权转移思想
后果:ap1将不再操作资源 ap1与资源断开联系
namespace bite {
//1:通过RAII的方式管理指针
//2:具有像指针的行为:解引用,->
//3:解决浅拷贝问题(深拷贝(不行))原因:资源是外部用户提供的,类只负责管理资源,没有申请资源的权利
//c++98 auto_ptr:资源转移
template<class T>
class auto_ptr {
public:
auto_ptr(T*ptr=nullptr)
:_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;
}
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);
ap1 = ap2;
}
int main() {
TestFunc();
return 0;
}
c++03:ap1将其堆资源释放的权利交给ap2来进行管理后果:可能会产生野指针
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) {
Release();
_ptr = ap._ptr;
_owner = ap._owner;
ap._owner = false;
}
return *this;
}
~auto_ptr() {
Release();
}
T& operator*() {
return *_ptr;
}
T* operator->() {
return _ptr;
}
private:
void Release() {
if (_ptr && _owner) {
delete _ptr;
}
}
private:
T* _ptr;
mutable bool _owner;
};
}
void TestFunc() {
bite::auto_ptr<int>ap1(new int);
bite::auto_ptr<int>ap2(ap1);
ap1 = ap2;
if (true) {
bite::auto_ptr<int>ap3(ap2);
}
*ap1 = 10;
*ap2 = 20;
//ap1和ap2为野指针,会造成程序崩溃
}
int main() {
TestFunc();
return 0;
}
建议:什么情况下都不要使用auto_ptr;
c++11:unique_ptr:RAII+operator*()/operator->()+独立管理资源,不能共享资源(不能进行拷贝)
c++98:拷贝构造函数与赋值运算符重载只声明不定义&权限为private c++11:拷贝构造函数与赋值运算符重载=delete原理:简单粗暴的防拷贝缺陷:对象之间不能共享资源
//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;
}
// //c++98
//private:
// unique_ptr(const unique_ptr<int>& up);
// unique_ptr<T>& operator=(const unique_ptr<int>& up);
//c++11
unique_ptr(const unique_ptr<int>& up) = delete;
unique_ptr<int>& operator=(const unique_ptr<int>& up) = delete;
private:
T* _ptr;
};
}
void TestFunc() {
bite::unique_ptr<int>up1(new int);
bite::unique_ptr<int>up2(up1);
up1 = up2;
}
int main() {
TestFunc();
return 0;
}
shared_ptr: RAII+operator*()/operator->()+引用计数==共享
原理:通过引用计数的方式来实现多个shared_ptr对象之间的共享资源
- shared_ptr在其内部,给每个资源都维护着一份计数,用来记录该份资源被几个对象共享
- 在对象被销毁时(也就是析构函数调用),就说明自己不用该资源了,对象的引用计数减一。
- 如果引用计数是0,就说明自己是最后一个使用该资源的对象们必须释放资源
- 如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放资源,否则其他对象就成野指针了
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() {
Release();
}
shared_ptr(const shared_ptr<T>& sp)
:_ptr(sp._ptr)
, _pCount(sp._pCount)
{
if (_ptr) {
++(*_pCount);
}
}
//sp1:未管理资源--直接与sp2共享
//sp1:单独管理资源--在与sp2共享前,必须先释放自己的资源
//sp1:与其他shared_ptr的对象已经共享资源--sp1将其计数减1
shared_ptr<T>& operator=(const shared_ptr<T>& sp) {
if (_ptr!=sp._ptr) {
Release();
//与sp共享
_ptr = sp._ptr;
_pCount = sp._pCount;
if (_ptr) {
++(*_pCount);
}
}
return *this;
}
T& operator*() {
return *_ptr;
}
T* operator->() {
return _ptr;
}
int usecount() {
return *_pCount;
}
private:
void Release() {
if (_ptr && 0 == (-- * _pCount)) {
delete _ptr;
delete _pCount;
}
}
private:
T* _ptr;
int* _pCount;
};
}
void TestFunc() {
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;
sp4 = sp1;//sp4未管理资源
sp3 = sp1;//sp3单独管理一份资源--释放旧资源,与sp1共享
sp1 = sp2;
}
template<class T>
class DFDel {
public:
void operator()(T*& p) {
if (p) {
delete p;
p = nullptr;
}
}
};
template<class T>
class Free {
public:
void operator()(T*& p) {
if (p) {
free(p);
p = nullptr;
}
}
};
class FClose {
public:
void operator()(FILE* p) {
if (p) {
fclose(p);
p = nullptr;
}
}
};
namespace bite {
template<class T,class DF=DFDel<T>>
class shared_ptr {
public:
shared_ptr(T* ptr = nullptr)
:_ptr(ptr)
, _pCount(nullptr)
{
if (_ptr) {
_pCount = new int(1);
}
}
~shared_ptr() {
Release();
}
shared_ptr(const shared_ptr<T>& sp)
:_ptr(sp._ptr)
, _pCount(sp._pCount)
{
if (_ptr) {
++(*_pCount);
}
}
shared_ptr<T>& operator=(const shared_ptr<T>& sp) {
if (_ptr != sp._ptr) {
Release();
//与sp共享
_ptr = sp._ptr;
_pCount = sp._pCount;
if (_ptr) {
++(*_pCount);
}
}
return *this;
}
T& operator*() {
return *_ptr;
}
T* operator->() {
return _ptr;
}
int usecount() {
return *_pCount;
}
private:
void Release() {
if (_ptr && 0 == (-- * _pCount)) {
//DF df;
//df(_ptr);
//等价于
DF()(_ptr);//DF是一个类型 DF():创建一个无名对象
delete _pCount;
}
}
private:
T* _ptr;
int* _pCount;
};
}
void TestFunc() {
bite::shared_ptr<int,Free<int>>sp1((int*)malloc(sizeof(int)));
bite::shared_ptr<FILE,FClose>sp2(fopen("666.txt", "rb"));
bite::shared_ptr<int>sp3(new int);
}
int main() {
TestFunc();
return 0;
}
缺陷:循环引用
//shared_ptr:循环引用问题
#include<memory>
struct ListNode {
ListNode(int data=int())
:_pre(nullptr)
,_next(nullptr)
,_data(data)
{
cout << "ListNode():" << this << endl;
}
~ListNode() {
cout << "~ListNode():" << this << endl;
}
shared_ptr<ListNode>_pre;
shared_ptr<ListNode>_next;
int _data;
};
void TestFunc() {
shared_ptr<ListNode>sp1(new ListNode(10));
shared_ptr<ListNode>sp2(new ListNode(20));
cout << sp1.use_count() << endl;
cout << sp2.use_count() << endl;
sp1->_next = sp2;
sp2->_next = sp1;
cout << sp1.use_count() << endl;
cout << sp2.use_count() << endl;
}
int main() {
TestFunc();
return 0;
}
循环引用分析:
- sp1和sp2两个智能指针对象指向两个结点,引用计数变为1,我们不需要手动delete
- sp1的next指向sp2,sp2的pre指向sp1,引用计数变为2
- sp1和sp2析构,引用计数减到1,但是_next还指向下一个节点,_pre还指向上一个节点
- 也就是说,_next析构了,sp1就释放了
- 也就是说,_pre析构了,sp2就释放了
- 但是_next属于sp1的成员,sp1释放了,_next才会析构,而sp1有_pre管理,_pre属于sp2成员,所以这就叫循环引用,谁也不会释放
解决方案
//shared_ptr:循环引用问题
//weak_ptr:不能单独管理资源,必须配合shared_ptr一起使用
// 因为weak_ptr作用:就是为了解决shared_ptr存在的循环引用问题而生的
#include<memory>
struct ListNode {
ListNode(int data=int())
:_data(data)
{
cout << "ListNode():" << this << endl;
}
~ListNode() {
cout << "~ListNode():" << this << endl;
}
weak_ptr<ListNode>_pre;
weak_ptr<ListNode>_next;
int _data;
};
void TestFunc() {
shared_ptr<ListNode>sp1(new ListNode(10));
shared_ptr<ListNode>sp2(new ListNode(20));
cout << sp1.use_count() << endl;
cout << sp2.use_count() << endl;
sp1->_next = sp2;
sp2->_next = sp1;
cout << sp1.use_count() << endl;
cout << sp2.use_count() << endl;
}
int main() {
//编译报错:weak_ptr不能单独管理资源
//weak_ptr<int>wp(new int);
TestFunc();
return 0;
}