RAII叫做资源获取即初始化,是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。 通常是将资源封装到一个类中,将资源管理的责任交给一个对象。
template<class T>
class smartprt
{
public:
smartprt(T* ptr)
:_ptr(ptr)
{}
smartptr(smartptr<T>&ptr)
:_ptr(ptr)
{}
~smartprt()
{
cout << "delete:" << ptr << endl;
delete _ptr; //释放内存
_ptr = nullptr;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
T* get()
{
return _ptr;
}
private:
T* _ptr;
};
测试
struct Date
{
int _year;
int _month;
int _day;
};
int main()
{
smartptr<int>ptr1(new int);
*ptr1 = 10;
cout << *ptr1 << endl;
smartptr<Date>date(new Date);
/*
这一步编译器进行了优化,原调用应该是
date.operator->()->_year; 编译器为了可读性
省略了operator->()的调用
*/
date->_year = 2022;
date->_month = 9;
date->_day = 22;
cout << "year:" << date->_year << endl;
cout << "_month:" << date->_month << endl;
cout << "_day:" << date->_day << endl;
return 0;
}

上面smartptr类,会出现二次析构问题。(比如我们拷贝一个指针,那么指针指向的内存可能被释放多次)
void func()
{
smartptr<Date>date1(new Date);
date1->_year = 2022;
date1->_month = 9;
date1->_day = 22;
smartptr<Date>date3 = date1;
}
int main()
{
func();
return 0;
}

C++98版本的库中就提供了auto_ptr的智能指针。其核心思想是将内存的管理权移交。【结果是无论如何拷贝,都只有一个指针可以对内存进行管理】。
下面实现一个简化版本的auto_ptr
template<class T>
class myauto_ptr
{
typedef myauto_ptr<T> self;
public:
//构造函数
myauto_ptr(T*ptr):_ptr(ptr)
{}
~myauto_ptr()
{
if (_ptr)
{
cout << "delete" << _ptr << endl;
delete _ptr;
_ptr = nullptr;
}
}
//拷贝构造,移交管理权
myauto_ptr(myauto_ptr<T>&au)
:_ptr(au._ptr)
{
au._ptr = nullptr;
}
//赋值运算符重载
myauto_ptr<T>& operator=(myauto_ptr<T>&aut)
{
_ptr = aut._ptr;
aut._ptr = nullptr;
return *this;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
T* get()
{
return _ptr;
}
private:
T* _ptr;
};
测试
void fun2()
{
myauto_ptr<Date>date1(new Date);
date1->_year = 2022;
date1->_month = 9;
date1->_day = 22;
myauto_ptr<Date>date2(date1);
cout << "date2->_year" << date2->_year << endl;
cout << "date2->month" << date2->_month << endl;
cout << "date2->day" << date2->_day << endl;
cout << "date1->_year" << date1->_year << endl;
cout << "date1->month" << date1->_month << endl;
cout << "date1->day" << date1->_day << endl;
}
int main()
{
fun2();
return 0;
}

表明只有一个指针具有管理和访问内存的权限
unique_ptr的实现原理:简单粗暴的防拷贝。
template<class T>
class myunique_ptr
{
public:
//构造函数
myunique_ptr(T* ptr) :_ptr(ptr)
{}
~myunique_ptr()
{
if (_ptr)
{
cout << "delete" << _ptr << endl;
delete _ptr;
_ptr = nullptr;
}
}
//防止拷贝
//方法一:使用关键字delete
//方法二:自己在类中声明并实现什么都不干的拷贝构造和赋值运算符重载
myunique_ptr(const myunique_ptr<T>& q) = delete;
myunique_ptr<T>& operator=(const myunique<T>& sp) = delete;
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
T* get()
{
return _ptr;
}
private:
T* _ptr;
};
C++中第一个好用的智能指针。
**shared_ptr的原理:是通过引用计数的方式来实现多个shared_ptr对象之间共享资源。 **
template<class t>
class myshared_ptr
{
public:
void release()
{
if (--(*_count) == 0 && _ptr)
{
cout << "delete" << _ptr << endl;
delete _ptr;
_ptr = nullptr;
delete _count;
_count = nullptr;
}
}
myshared_ptr(t* ptr)
:_ptr(ptr)
,_count(new int(1))
{}
~myshared_ptr()
{
release();
}
t& operator*()
{
return *_ptr;
}
t* operator->()
{
return _ptr;
}
t* get()
{
return _ptr;
}
myshared_ptr(const myshared_ptr<t>& sp)
:_ptr(sp._ptr)
, _count(sp._count)
{
(*_count)++;
}
//赋值运算符重载
myshared_ptr<t>& operator=(const myshared_ptr<t>& sp)
{
//if (this != &sp)
if (_ptr != sp._ptr)
{
release();
_ptr=sp._ptr;
_count =sp._count;
++(*_count);
}
return *this;
}
private:
t* _ptr;
int* _count;
};
测试
void fun3()
{
myshared_ptr<Date>date1(new Date);
date1->_year = 2022;
date1->_month = 9;
date1->_day = 22;
myshared_ptr<Date>date2(date1);
myshared_ptr<Date>date3 = date1;
cout << "date2->_year" << date2->_year << endl;
cout << "date2->month" << date2->_month << endl;
cout << "date2->day" << date2->_day << endl;
cout << "date1->_year" << date1->_year << endl;
cout << "date1->month" << date1->_month << endl;
cout << "date1->day" << date1->_day << endl;
}
int main()
{
fun3();
return 0;
}

解决方法:对_count的操作都加锁
template<class t>
class myshared_ptr
{
public:
void release()
{
_pmtx->lock();
if (--(*_count) == 0 && _ptr)
{
cout << "delete" << _ptr << endl;
delete _ptr;
_ptr = nullptr;
delete _count;
_count = nullptr;
}
_pmtx->unlock();
}
myshared_ptr(t* ptr)
:_ptr(ptr)
,_count(new int(1))
, _pmtx(new mutex)
{}
~myshared_ptr()
{
release();
}
t& operator*()
{
return *_ptr;
}
t* operator->()
{
return _ptr;
}
t* get()
{
return _ptr;
}
myshared_ptr(const myshared_ptr<t>& sp)
:_ptr(sp._ptr)
, _count(sp._count)
, _pmtx(sp._pmtx)
{
_pmtx->lock();
(*_count)++;
_pmtx->unlock();
}
//赋值运算符重载
myshared_ptr<t>& operator=(const myshared_ptr<t>& sp)
{
//if (this != &sp)
if (_ptr != sp._ptr)
{
release();
_ptr=sp._ptr;
_count =sp._count;
_pmtx->lock();
++(*_count);
_pmtx->unlock();
}
return *this;
}
private:
t* _ptr;
int* _count;
mutex* _pmtx;
};
shared_ptr虽然功能交其他的智能指针功能更加强大,但是同样有其问题,其中一个就是循环引用的问题。如:
struct ListNode
{
std::shared_ptr<ListNode> prev = nullptr;
std::shared_ptr<ListNode> next = nullptr;
int _val = 0;
~ListNode()
{
cout << "~ListNode()" << endl;
}
};
int main()
{
std::shared_ptr<ListNode>ptr1(new ListNode);
std::shared_ptr<ListNode>ptr2(new ListNode);
ptr1->next = ptr2;
ptr2->prev = ptr1;
return 0;
}
按照预期,ptr1和ptr2析构时会分别释放对应的ListNode结构,但结果是并没有释放内存

shared_ptr的底层是实现的引用计数,上面的代码逻辑为:

当main函数结束,ptr1和ptr2发生析构时:

为了解决shared_ptr出现的循环引用问题,C++11引入了weak_ptr。
weak_ptr可以访问对应的内存,但是不参与资源的管理。
struct ListNode
{
std::weak_ptr<ListNode> prev ;
std::weak_ptr<ListNode> next ;
int _val = 0;
~ListNode()
{
cout << "~ListNode()" << endl;
}
};
int main()
{
std::shared_ptr<ListNode>ptr1(new ListNode);
std::shared_ptr<ListNode>ptr2(new ListNode);
ptr1->next = ptr2;
ptr2->prev = ptr1;
return 0;
}

weak_ptr的底层实现很简单,可以访问对应的内存,但是不参与资源的管理。
template <class T>
class myweak_ptr
{
myweak_ptr()
:_ptr(nullptr)
{}
myweak_ptr(const shared_ptr<T>& sp)
:_ptr(sp.get())
{}
myweak_ptr<T>& operator(const shared_ptr<T>&sp)
{
if(_ptr!=sp.get())
{
_ptr=sp.get();
}
return *this;
}
private:
T* _ptr;
}
智能指针默认释放资源的方式是delete。
由于无法删除数组类型的指针或者FILE*的类型,所以出现了定制删除器。
struct Date
{
~Date()
{
cout << "~Date()" << endl;
}
private:
int _year = 1;
int _month = 1;
int _day = 1;
};
int main()
{
std::unique_ptr<Date>date1(new Date);
std::unique_ptr<Date>date2(new Date[10]);
std::unique_ptr<Date>date3((Date*)malloc(sizeof(Date) * 5));
//std::unique_ptrptr1((FILE*)fopen("test.cpp", "r"));
return 0;
}

发生了内存泄漏
定制删除器的底层就是一个仿函数
struct Date
{
~Date()
{
cout << "~Date()" << endl;
}
private:
int _year = 1;
int _month = 1;
int _day = 1;
};
template <class T>
struct del
{
void operator()(T* ptr)
{
delete[] ptr;
}
};
template <class T>
struct freed
{
void operator()(T* ptr)
{
free(ptr);
}
};
int main()
{
std::unique_ptr<Date>date1(new Date);
std::unique_ptr<Date,del<Date>>date2(new Date[10]);
std::unique_ptr<Date,freed<Date>>date3((Date*)malloc(sizeof(Date) * 10));
//std::unique_ptrptr1((FILE*)fopen("test.cpp", "r"));
return 0;
}

delete的底层是先调用析构函数,在调用operator delete。而operator delete又封装了free()