智能指针
1、智能指针解决的问题
1、内存泄漏问题,开辟出来的空间没有进行释放,存在内存泄漏
2、异常安全问题,若是malloc和free之间若是存在抛出异常,会出现内存泄漏
2、智能指针的使用及原理
2.1、RAII技术
RAII(Resource ACquisition Is Initialization)是利用对象生命周期来控制程序资源(内存,文件句柄,网络连接,互斥量)的技术
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内保持有效,直至该对象被析构,此时释放资源
2.2、RAII技术的优点
1、不需要显示释放资源
2、对象所需的资源在其生命周期内始终有效
2.3、使用RAII技术设计SmartPtr
#include <iostream>
#include <vector>
using namespace std;
//使用RAII技术设计SmartPtr类
template<class T>
class SmartPtr
{
public:
SmartPtr(T* ptr = nullptr)
:m_ptr(ptr)
{
}
~SmartPtr()
{
if (m_ptr)
{
delete m_ptr;
}
}
private:
T* m_ptr;
};
void dealMergeSort(int* a, int start, int end, int* tmp)
{
if (start >= end)
{
return;
}
int mid = (start + end) / 2;
dealMergeSort(a, start, mid, tmp);
dealMergeSort(a, mid + 1, end, tmp);
int p = start;
int q = mid + 1;
int index = start;
while (p <= mid && q <= end)
{
if (a[p] < a[q])
{
tmp[index] = a[p];
p++;
}
else
{
tmp[index] = a[q];
q++;
}
index++;
}
for (; p <= mid; p++)
{
tmp[index] = a[p];
index++;
}
for (; q <= end; q++)
{
tmp[index] = a[q];
index++;
}
for (int i = start; i < end; i++)
{
a[i] = tmp[i];
}
}
void MergeSort(int* a, int n)
{
int* tmp = (int*)malloc(sizeof(int)*n);
SmartPtr<int> sp(tmp);
dealMergeSort(a, 0, n - 1, tmp);
vector<int> v(100000000, 10);
}
int main()
{
try
{
int a[5] = { 4, 5, 2, 3, 1 };
MergeSort(a, 5);
}
catch (const exception& e)
{
cout << e.what() << endl;
}
catch (...)
{
cout << "Unknow erro" << endl;
}
system("pause");
return 0;
}
2.4、智能指针原理
1、使用了RAII技术
2、重载了*和->运算符
#include <iostream>
using namespace std;
//智能指针需要具备指针的行为,如解引用,箭头访问
//智能指针使用了RAII技术
template<class T>
class SmartPtr
{
public:
SmartPtr(T* ptr = nullptr)
:m_ptr(ptr)
{
}
~SmartPtr()
{
if (m_ptr)
{
delete m_ptr;
}
}
T& operator * ()
{
return *m_ptr;
}
T* operator -> ()
{
return m_ptr;
}
private:
T* m_ptr;
};
struct Date
{
int m_year;
int m_month;
int m_day;
};
int main()
{
SmartPtr<int> sp(new int);
*sp = 10;
cout << *sp << endl;
SmartPtr<Date> sparray(new Date);
sparray->m_year = 2020;
sparray->m_month = 4;
sparray->m_day = 27;
system("pause");
return 0;
}
3、auto_ptr
3.1、auto_ptr的使用
#include <iostream>
#include <memory>
using namespace std;
class Date
{
public:
Date()
{
cout << "Date()" << endl;
}
~Date()
{
cout << "~Date()" << endl;
}
public:
int m_year;
int m_month;
int m_day;
};
int main()
{
auto_ptr<Date> ap(new Date);
auto_ptr<Date> copy(ap);
//当对象拷贝或者赋值后,前面的对象就悬空了
ap->m_year = 2020;
ap->m_month = 4;
ap->m_day = 28;
system("pause");
return 0;
}
3.2、auto_ptr的模拟实现
管理权转移
#include <iostream>
using namespace std;
template<class T>
class AutoPtr
{
public:
AutoPtr(T* ptr = nullptr)
:m_ptr(ptr)
{
}
~AutoPtr()
{
if (m_ptr)
{
delete m_ptr;
}
}
AutoPtr<T>& operator = (AutoPtr<T>& ap)
{
if (*this != ap)
{
if (m_ptr)
{
//释放当前对象的资源
delete m_ptr;
}
//将ap对象的资源转移到当前对象
m_ptr = ap.m_ptr;
ap.m_ptr = nullptr;
}
return *this;
}
T& operator * ()
{
return *m_ptr;
}
T* operator -> ()
{
return m_ptr;
}
private:
T* m_ptr;
};
struct Date
{
int m_year;
int m_month;
int m_day;
};
int main()
{
AutoPtr<Date> ap(new Date);
AutoPtr<Date> copy(ap);
//ap对象访问资源时就会出现问题
ap->m_year = 2020;
ap->m_month = 4;
ap->m_day = 28;
system("pause");
return 0;
}
4、unique_ptr
4.1、unique_ptr的使用
#include <iostream>
#include <memory>
using namespace std;
struct Date
{
int m_year;
int m_month;
int m_day;
};
int main()
{
unique_ptr<Date> up(new Date);
//C++11防止拷贝和赋值
//unique_ptr<Date> copy(up);
up->m_year = 2020;
up->m_month = 4;
up->m_day = 28;
system("pause");
return 0;
}
4.2、unique_ptr的模拟实现
防拷贝
#include <iostream>
using namespace std;
template<class T>
class UniquePtr
{
public:
UniquePtr(T* ptr = nullptr)
:m_ptr(ptr)
{
}
~UniquePtr()
{
if (m_ptr)
{
delete m_ptr;
}
}
T& operator * ()
{
return *m_ptr;
}
T* operator -> ()
{
return m_ptr;
}
private:
//C++98防拷贝和赋值(拷贝和赋值私有化,只声明)
UniquePtr(UniquePtr<T> const &);
UniquePtr& operator = (UniquePtr<T> const &);
//C++11防拷贝和赋值用delete
UniquePtr(UniquePtr<T> const &) = delete;
UniquePtr& operator = (UniquePtr<T> const &) = delete;
private:
T* m_ptr;
};
struct Date
{
int m_year;
int m_month;
int m_day;
};
int main()
{
UniquePtr<Date> up(new Date);
//UniquePtr<Date> copy(up);
up->m_year = 2020;
up->m_month = 4;
up->m_day = 28;
system("pause");
return 0;
}
5、shared_ptr
5.1、shared_ptr的使用
#include <iostream>
#include <memory>
using namespace std;
struct Date
{
int m_year;
int m_month;
int m_day;
};
int main()
{
//shared_ptr通过引用计数支持智能指针对象的拷贝
shared_ptr<Date> sp(new Date);
shared_ptr<Date> copy(sp);
cout << "ref count:" << sp.use_count() << endl;
cout << "ref count:" << copy.use_count() << endl;
system("pause");
return 0;
}
5.2、shared_ptr的模拟实现
通过引用计数支持智能指针对象的拷贝
shared_ptr的原理
shared_ptr的原理:通过引用计数的方式来实现多个shared_ptr对象之间的共享资源
1、shared_ptr的内部,给每个资源都维护着一份计数,用来记录该资源被几个对象共享
2、在对象被销毁的时候,对象的引用计数减一
3、若是引用计数是0,则证明该对象是最后一个使用该资源的对象,用完必须释放资源
4、若不是0,则不能释放该资源否则会变成野指针
#include <iostream>
#include <mutex>
using namespace std;
template <class T>
class SharedPtr
{
public:
SharedPtr(T* ptr = nullptr)
:m_ptr(ptr)
, m_pRefCount(new int(1))
, m_pMutex(new mutex)
{
}
~SharedPtr()
{
Release();
}
SharedPtr(const SharedPtr<T>& sp)
:m_pRefCount(sp.m_pRefCount)
, m_ptr(sp.m_ptr)
, m_pMutex(sp.m_pMutex)
{
AddRefCount();
}
SharedPtr& operator = (const SharedPtr<T>& sp)
{
if (this != &sp)
{
Release();
m_pRefCount = sp.m_pRefCount;
m_ptr = sp.m_ptr;
m_pMutex = sp.m_pMutex;
AddRefCount();
}
return *this;
}
T& operator * ()
{
return *m_ptr;
}
T* operator -> ()
{
return m_ptr;
}
int UseCount()
{
return *m_pRefCount;
}
T* Get()
{
return m_ptr;
}
void AddRefCount()
{
m_pMutex->lock();
(*m_pRefCount)++;
m_pMutex->unlock();
}
private:
void Release()
{
bool deleteflag = false;
m_pMutex->lock();
if ((*m_pRefCount)-- == 0)
{
delete m_ptr;
delete m_pRefCount;
deleteflag = true;
}
m_pMutex->unlock();
if (deleteflag == true)
{
delete m_pMutex;
}
}
private:
int* m_pRefCount;
T* m_ptr;
mutex* m_pMutex;
};
int main()
{
SharedPtr<int> sp1(new int(10));
SharedPtr<int> sp2(sp1);
*sp2 = 20;
cout << sp1.UseCount() << endl;
cout << sp2.UseCount() << endl;
SharedPtr<int> sp3(new int(10));
sp2 = sp3;
cout << sp1.UseCount() << endl;
cout << sp2.UseCount() << endl;
cout << sp3.UseCount() << endl;
sp1 = sp3;
cout << sp1.UseCount() << endl;
cout << sp2.UseCount() << endl;
cout << sp3.UseCount() << endl;
system("pause");
return 0;
}
5.3、shared_ptr的异常安全
1、智能指针对象中引用计数是多个智能指针对象共享的,两个线程中智能指针的引用计数同时++或–,这个操作不是原子的,引用计数原来是1,++了两次,可能还是2,这样会导致资源未释放或者程序崩溃,–需要加锁
2、智能指针管理的对象存放在堆上,两个线程中同时去访问,会导致线程安全问题
#include <iostream>
#include <memory>
#include <thread>
using namespace std;
struct Date
{
int m_year;
int m_month;
int m_day;
};
void SharedPtrFunc(shared_ptr<Date>& sp, size_t n)
{
cout << sp.get() << endl;
for (size_t i = 0; i < n; i++)
{
shared_ptr<Date> copy(sp);
copy->m_year++;
copy->m_month++;
copy->m_day++;
}
}
int main()
{
shared_ptr<Date> p(new Date);
cout << p.get() << endl;
const size_t n = 100;
thread t1(SharedPtrFunc, p, n);
thread t2(SharedPtrFunc, p, n);
t1.join();
t2.join();
cout << p->m_year << endl;
cout << p->m_month << endl;
cout << p->m_day << endl;
system("pause");
return 0;
}
5.4、shared_ptr的循环引用
循环引用的分析
1、node1和node2两个智能指针对象指向两个节点,引用计数变成1,不需要手动delete
2、node1的m_pnext指向node2,node2的m_prev指向node1,引用计数变成2
3、node1和node2析构,引用计数减1,但是m_next还是指向下一个节点,但是m_prev还是指向上一个节点
4、m_next是node1的成员,m_prev是node2的成员,只有node1和node2析构,m_next和m_prev才会析构,这就构成了循环引用,谁都不会释放
#include <iostream>
#include <memory>
using namespace std;
class ListNode
{
public:
int m_data;
shared_ptr<ListNode> m_prev;
shared_ptr<ListNode> m_next;
public:
~ListNode()
{
cout << "~ListNode()" << endl;
}
};
int main()
{
shared_ptr<ListNode> node1(new ListNode);
shared_ptr<ListNode> node2(new ListNode);
cout << node1.use_count() << endl;
cout << node2.use_count() << endl;
node1->m_next = node2;
node2->m_prev = node1;
cout << node1.use_count() << endl;
cout << node2.use_count() << endl;
system("pause");
return 0;
}
5.5、shared_ptr的循环引用的解决
将shared_ptr改成weak_ptr
node1->m_next = node2 node2->m_prev = node1 此时的weak_ptr的m_next和m_prev不会增加node1和node2的引用计数
#include <iostream>
#include <memory>
using namespace std;
class ListNode
{
public:
int m_data;
weak_ptr<ListNode> m_prev;
weak_ptr<ListNode> m_next;
public:
~ListNode()
{
cout << "~ListNode()" << endl;
}
};
int main()
{
shared_ptr<ListNode> node1(new ListNode);
shared_ptr<ListNode> node2(new ListNode);
cout << node1.use_count() << endl;
cout << node2.use_count() << endl;
node1->m_next = node2;
node2->m_prev = node1;
cout << node1.use_count() << endl;
cout << node2.use_count() << endl;
system("pause");
return 0;
}
5.6、shared_ptr管理非new对象
shared_ptr设计了删除器来解决非new对象
#include <iostream>
#include <memory>
using namespace std;
template<class T>
class FreeFunc
{
public:
void operator () (T* ptr)
{
cout << "Free:" << ptr << endl;
free(ptr);
}
};
template<class T>
class DeleteArrayFunc
{
public:
void operator () (T* ptr)
{
cout << "delete[]" << ptr << endl;
delete[] ptr;
}
};
int main()
{
FreeFunc<int> freeFunc;
shared_ptr<int> sp1((int*)malloc(sizeof(int)), freeFunc);
DeleteArrayFunc<int> deleteArrayFunc;
shared_ptr<int> sp2((int*)malloc(sizeof(int)), deleteArrayFunc);
system("pause");
return 0;
}
6、RAII技术设计守卫锁
用RAII技术可以设计守卫锁,防止异常安全导致的死锁
#include <iostream>
#include <mutex>
#include <thread>
using namespace std;
template<class T>
class LockGuard
{
public:
LockGuard(mutex& mtx)
:m_mutex(mtx)
{
m_mutex.lock();
}
~LockGuard()
{
m_mutex.unlock();
}
LockGuard(const LockGuard<mutex>&) = delete;
private:
mutex& m_mutex;
};
mutex mtx;
static int n = 0;
void Func()
{
for (size_t i = 0; i < 100000; i++)
{
LockGuard<mutex> lock(mtx);
n++;
}
}
int main()
{
int begin = clock();
thread t1(Func);
thread t2(Func);
t1.join();
t2.join();
int end = clock();
cout << n << endl;
cout << "cost time:" << end - begin << endl;
system("pause");
return 0;
}