智能指针喵喵喵
智能指针的使用场景
正常情况下我们new出来的资源,经过delete以后会正确释放,但是因为抛异常,会导致后面的delete没有执行,所以内存就泄露了,所以我们需要在new以后捕获异常,捕获到异常后delete内存,再把异常抛出来。但是new本身也会抛异常,除法函数也会抛异常,处理起来很麻烦智能指针解决这样的问题非常好。
/////////////////////////////////////////////场景////////////////////////////////////
double Divide(int a, int b)
{
if (b == 0)
throw "Divide by zero condition";
else
{
return (double)a / (double)b;
}
}
void Func()
{
// 这⾥可以看到如果发⽣除0错误抛出异常,另外下⾯的ptr1和ptr2没有得到释放。
// 所以这⾥捕获异常后并不处理异常,异常还是交给外⾯处理,这⾥捕获了再重新抛出去。
// 但是如果ptr2new的时候抛异常呢,就还需要套⼀层捕获释放逻辑,这⾥更好解决⽅案
// 是智能指针,否则代码太戳了
int* ptr1 = new int[10];
int* ptr2 = new int[10];
try
{
int len, time;
cin >> len >> time;
Divide(len, time);
}
catch (...)
{
delete[] ptr1;
delete[] ptr2;
cout << "ptr1" << endl;
cout << "ptr2" << endl;
throw;
}
delete[] ptr1;
delete[] ptr2;
}
int main()
{
try
{
Func();
}
catch(const char* errmsg)
{
cout << errmsg << endl;
}
catch (const exception& e)
{
cout << e.what() << endl;
}
catch (...)
{
}
return 0;
}
RAII和智能指针的设计思路
RAII资源获得立即初始化,它是一种管理类的设计思想,本质是一种利用生命周期来管理获取到的动态资源,避免资源泄露,这里的资源可以说内存,文件指针,网络连接,互斥锁等等。
RAII在获取到一个资源时把资源委托给一个对象,控制对资源的访问,资源在对象生命周期内始终保持有效,最后对象在析构的时候释放资源,保障了资源的正常释放,避免了资源泄露问题。
智能指针除了要满足RAII思路,还要方便访问资源,所以还会像迭代器重载一些操作符operator*/operator->/operator[],方便访问资源。
template<class T>
class SmartPtr
{
public:
SmartPtr(T* ptr)
:_ptr(ptr)
{ }
~SmartPtr()
{
cout << "delete [] _ptr" << endl;
delete[] _ptr;
}
//返回一个值
T& operator*()
{
return *_ptr;
}
T& operator[](size_t i)
{
return _ptr[i];
}
//返回一个指针
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
double Divide(int a, int b)
{
if (b == 0)
throw "Divide by zero condition";
else
{
return (double)a / (double)b;
}
}
void Func()
{
// 这⾥可以看到如果发⽣除0错误抛出异常,另外下⾯的ptr1和ptr2没有得到释放。
// 所以这⾥捕获异常后并不处理异常,异常还是交给外⾯处理,这⾥捕获了再重新抛出去。
// 但是如果ptr2new的时候抛异常呢,就还需要套⼀层捕获释放逻辑,这⾥更好解决⽅案
// 是智能指针,否则代码太戳了
SmartPtr<int> ptr1 = new int[10];
SmartPtr<int> ptr2 = new int[10];
int len, time;
cin >> len >> time;
Divide(len, time);
ptr1[0] = 10;
cout << *(ptr1) << endl;
}
int main()
{
try
{
Func();
}
catch(const char* errmsg)
{
cout << errmsg << endl;
}
catch (const exception& e)
{
cout << e.what() << endl;
}
catch (...)
{
}
return 0;
}
C++标准库智能指针的使用
头文件<memory>
有多种智能指针,原理上主要是智能指针解决拷贝时的思路不同。
C++98,auto_ptr,特点是拷贝时会把被拷贝对象的资源管理权转让给拷贝对象。它会导致被拷贝对象悬空,访问以后会导致报错。
C++11,unique_ptr,唯一指针,特点不支持拷贝,只支持移动。
C++11,shared_ptr,共享指针,特点可以拷贝,也可以移动。底层是引用计数实现的。
C++11,weak_ptr,弱指针,它不会直接管理资源,解决shared_ptr的循环引用导致的内存泄露的问题。
智能指针析构时,默认用delete来释放资源。如果资源不是 new出来的,交给智能指针来管理,析构就会奔溃。
智能指针在构造时给一个删除器,删除器本质是一个可调用对象,这个可调用对象中实现你释放资源的方式,当智能指针构造时,给了定制的删除器,在智能指针析构的时候,就会调用删除器去释放资源。
/////////////////////////////////////////////////////C++标准库的指针/////////////////////////////////////////////////////////
struct Date
{
int _year;
int _month;
int _day;
//Date() = default;
Date(int year=1, int month=1, int day=1)
:_year(year)
, _month(month)
, _day(day)
{
}
~Date()
{
cout << "~Date()" << endl;
}
};
int main()
{
//构造函数只能如此
auto_ptr<Date> ap1(new Date);
//管理权转让给ap2 ap1悬空
auto_ptr<Date> ap2(ap1);
//编译时不报错 程序会奔溃
//ap1->_year++;
unique_ptr<Date> up1(new Date);
//不支持拷贝
//unique_ptr<Date> up2(up1);
//支持移动 移动后的up1也会悬空
unique_ptr<Date> up3(move(up1));
shared_ptr<Date> sp1(new Date);
//支持拷贝 支持移动
shared_ptr<Date> sp2(sp1);
//move 以后也会悬空
shared_ptr<Date> sp3(sp2);
cout << sp1.use_count() << endl;
sp1->_year++;
cout << sp1->_year << endl;
cout << sp2->_year << endl;
cout << sp3->_year << endl;
return 0;
}
class Fclose
{
public:
void operator()(FILE* ptr)
{
cout << "Fclose" << endl;
fclose(ptr);
}
};
//lambda表达式
//函数指针
template<class T>
void DeleteArrayFunc(T* ptr)
{
delete[] ptr;
}
//仿函数对象
template<class T>
class DeleteArray
{
public:
void operator()(T* ptr)
{
delete[] ptr;
}
};
//int main()
//{
// ////////////////////////////////////////////////shared_ptr//////////////////////////////////////////////
// // 定义删除器建议lambda表达式
// //std::shared_ptr<Date>sp1(new Date);
// ////默认是用delete来释放资源
// //std::shared_ptr<Date[]>sp2(new Date[10]);
//
// //std::shared_ptr<Date>sp5(new Date[10], DeleteArrayFunc<Date>); //模板的函数指针
// //std::shared_ptr<Date>sp6(new Date[10], [](Date* ptr) {delete[] ptr; }); //lambda表达式
// //std::shared_ptr<Date>sp6(new Date[10], DeleteArray<Date>()); //仿函数对象
//
//
// //std::shared_ptr<FILE>sp3(fopen("Test.cpp", "r"), Fclose());
// //std::shared_ptr<FILE>sp4(fopen("Test.cpp", "r"), [](FILE* pf) {fclose(pf); });
//
//
//
// ////////////////////////////////////////////////unique_ptr//////////////////////////////////////////////
// //定制删除器建议仿函数
// //std::unique_ptr<Date>up1(new Date);
// //std::unique_ptr<Date[]>up2(new Date[10]);
//
//
//
//
//
// //auto deleteArray = [](Date* ptr) {delete[] ptr; };
// //std::unique_ptr<Date, decltype(deleteArray)>up4(new Date[10],deleteArray); //lambda表达式
//
//
// //std::unique_ptr<Date, DeleteArray<Date>>up5(new Date[10]); //仿函数对象
//
//
//
// //std::unique_ptr<Date>up5(new Date[10]);
//
// //std::unique_ptr<FILE, Fclose>up6(fopen("test.cpp", "r"), Fclose()); //仿函数对象
// //std::unique_ptr<FILE, Fclose>up7(fopen("test.cpp", "r"));
//
//
// //auto pf = [](FILE* pf) {cout<<"fclose" << endl; fclose(pf); }; //lambda表达式
// //std::unique_ptr<FILE, decltype(pf) >up8(fopen("Test.cpp", "r"),pf);
// return 0;
//}
#include<functional>
namespace jiege
{
template<class T>
class auto_ptr
{
public:
auto_ptr(T* ptr)
:_ptr(ptr)
{ }
~auto_ptr()
{
if (_ptr)
{
delete _ptr;
}
}
//p1(p2)
auto_ptr(const auto_ptr<T>& sp)
:_ptr(sp._ptr)
{
sp._ptr = nullptr;
}
//p1=p2
auto_ptr<T>& operator=(auto_ptr<T>& sp)
{
if (this != sp)
{
_ptr = sp;
sp._ptr = nullptr;
}
return *this;
}
//像指针一样的使用
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
template<class T>
class unique_ptr
{
public:
unique_ptr(T* ptr)
:_ptr(ptr)
{
}
~unique_ptr()
{
if (_ptr)
{
delete[] _ptr;
}
}
//删除拷贝和拷贝赋值
unique_ptr(const unique_ptr<T>& sp) = delete;
unique_ptr<T>& operator=(const unique_ptr<T>& sp) = delete;
unique_ptr(unique_ptr<T>&& sp)
:_ptr(sp._ptr)
{
_ptr = nullptr;
}
//p1=p2
unique_ptr<T>& operator=(unique_ptr<T>&& sp)
{
if (this != sp)
{
//释放原来的资源
delete _ptr;
_ptr = sp;
sp._ptr = nullptr;
}
return *this;
}
//像指针一样的使用
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
template<class T>
class shared_ptr
{
public:
explicit shared_ptr(T* ptr)
:_ptr(ptr)
,_pcount(new atomic<int>int(1))
{ }
template<class D>
explicit shared_ptr(T* ptr,D del)
: _ptr(ptr)
, _pcount(new atomic<int>int(1))
,_del(del)
{
}
~shared_ptr()
{
if (--(*_pcount)==0)
{
_del(_ptr);
delete _pcount;
}
}
//p2(p1)共同管理资源
shared_ptr(const shared_ptr<T>& sp)//
:_ptr(sp._ptr)
, _pcount(sp._pcount)
{
++(*_pcount);
}
//sp1=sp4 //sp2原来管理一份资源
shared_ptr<T>& operator=(const shared_ptr<T>& sp)
{
if (_ptr != sp._ptr)
{
if (--(*_pcount) == 0)
{
delete _ptr;
delete _pcount;
}
_ptr = sp._ptr;
_pcount = sp._pcount;
++(*_pcount);
}
return *this;
}
//sp2 (sp1)
shared_ptr(shared_ptr<T>&& sp)
:_ptr(sp._ptr)
, _pcount(sp._pcount)
{
delete sp._ptr;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
int use_count()
{
return *_pcount;
}
private:
T* _ptr;
//int* _pcount;
atomic<int>* _pcount;
function<void(T*)>_del = [](T* ptr) {delete ptr; };
};
}
struct Date
{
int _year;
int _month;
int _day;
//Date() = default;
Date(int year=1, int month=1, int day=1)
:_year(year)
, _month(month)
, _day(day)
{
}
~Date()
{
cout << "~Date()" << endl;
}
};
//int main()
//{
// jiege::shared_ptr<Date>sp1(new Date);
//
// jiege::shared_ptr<Date>sp2(new Date[10], [](Date* ptr) {delete[] ptr; });
//
// return 0;
//}
//int main()
//{
// jiege::shared_ptr<Date> sp1(new Date);
// jiege::shared_ptr<Date> sp2(sp1);
// jiege::shared_ptr<Date> sp3 = sp2;
//
// jiege::shared_ptr<Date> sp4(new Date);
//
// sp1 = sp4;
// //sp1 = sp2;
// return 0;
//}
int main()
{
std::shared_ptr<Date>sp1(new Date);
std::shared_ptr<Date>sp2 = make_shared<Date>(2024, 3, 27);//用初始化资源对象的值直接构造
auto sp3 = make_shared<Date>(2025, 12, 19);
//if(sp1.operator bool()如果智能指针对象是⼀个空对象没有管理资源,则返回false,否则返回true,意味着我们可以直接把智能指针对象给if判断是否为空。
if (sp1)
cout << "sp1 is not nullptr" << endl;
std::shared_ptr<Date> sp4;
if (!sp4)
cout << "sp2 is nullptr" << endl;
//普通指针不能向智能指针转换 explicit
//std::shared_ptr < Date > sp5= new Date(2025, 12, 19);
return 0;
}
shared_ptr和weak_ptr循环引用和解决方式
循环引用场景

struct ListNode
{
int _data;
//ListNode* _next;
//ListNode* _prev;//n1->next=n2; n2是智能指针 n1->next是原生指针 互相不存在抓换关系
//std::shared_ptr<ListNode> _next;
//std::shared_ptr<ListNode> _prev;
//当n1->next=n2;weak_ptr 不增加n2引用计数 不参数资源的管理但会指向资源 就不会形成循环引用
//
std::weak_ptr<ListNode> _next;
std::weak_ptr<ListNode> _prev;
~ListNode()
{
cout << "~ListNode()" << endl;
}
};
//int main()
//{
// std::shared_ptr<ListNode> n1(new ListNode);
// std::shared_ptr<ListNode> n2(new ListNode);
//
// n1->_next = n2;
// n2->_prev = n1;
//
// cout << n1.use_count() << endl;
// cout << n2.use_count() << endl;
//
// return 0;
//}
weak_ptr
weak_ptr不支持RAII,也不支持访问资源,weak_ptr构造,不支持绑定资源,只支持绑定到shared_ptr,但是不会增加引用计数,就解决了上述问题。
int main()
{
std::shared_ptr<string>sp1(new string("11111111111111111"));
std::shared_ptr<string>sp2(sp1);
std::weak_ptr<string>wp = sp1;
cout << wp.expired() << endl;
cout << wp.use_count() << endl;
sp1 = make_shared<string>("22222222222222");
auto sp3 = wp.lock();
cout << wp.expired() << endl;
cout << wp.use_count() << endl;
return 0;
}
shared_ptr线程安全问题
#include<thread>
struct AA
{
int _a1 = 0;
int _a2 = 0;
~AA()
{
cout << "~AA()" << endl;
}
};
int main()
{
jiege::shared_ptr<AA> p(new AA);
const size_t n = 100000;
mutex mtx;
auto func = [&]()
{
for (size_t i = 0; i < n; ++i)
{
// 这⾥智能指针拷⻉会++计数
jiege::shared_ptr<AA> copy(p);
{
unique_lock<mutex> lk(mtx);
copy->_a1++;
copy->_a2++;
}
}
};
thread t1(func);
thread t2(func);
t1.join();
t2.join();
cout << p->_a1 << endl;
cout << p->_a2 << endl;
cout << p.use_count() << endl;
return 0;
}
Boost社区
内存泄露
如何避免内存泄漏
更多推荐

所有评论(0)