一、智能指针概念:
(1)内存泄露及其危害:
- 内存泄漏:内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。
- 危害:长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死。
- 检测内存泄露的工具:
①windows下检测:VLD;
②LInux下检测:
(2)智能指针原理:
- 智能指针:它具有指针的行为,指针可以解引用,也可以通过->去访问所指空间中的内容,因此智能指针需要将* 、->重载下,才可让其像指针一样去使用。
- 智能指针的原理:
①. RAII特性;
②. 重载operator*和opertaor->,具有像指针一样的行为。
二、RAII机制:
(一)RAII中的智能指针:
- RAII:**是利用对象的生命周期来控制程序资源的。**即:在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。
- 好处:
①不需要显式地释放资源。
②采用这种方式,对象所需的资源在其生命期内始终保持有效。 - 利用RAII机制设计Smartptr类:
代码如下:
template<class T>
class SmartPtr
{
public:
SmartPtr(T* ptr = nullptr) :_ptr(ptr)//构造函数
{
}
~SmartPtr()//析构函数
{
if (_ptr)
delete _ptr;
}
private:
T* _ptr;
};
void MergeSort(int* a, int n)
{
int* tmp = (int*)malloc(sizeof(int)*n);
// 讲tmp指针委托给了sp对象,
SmartPtr<int> sp(tmp);
}
int main()
{
try {
int a[5] = { 4, 5, 2, 3, 1 };
MergeSort(a, 5);
}
catch (const exception& e)
{
cout << e.what() << endl;
}
return 0;
}
(二)RAII机制设计守卫锁:
代码如下:
#include<iostream>
#include<thread>
#include<mutex>
using namespace std;
mutex mut;
static int a = 0;
template<class T>
class LockMutex
{
public:
LockMutemx(mutex& _mut) :_mutex(_mut)//
{
_mutex.lock();
}
~LockMutex()
{
_mutex.unlock();
}
LockMutex(const LockMutex<mutex>&) = delete;//没有拷贝构造。
private:
mutex& _mutex;//互斥锁
};
void fun()
{
for (int i = 0; i < 100; ++i)
{
LockMutex<mutex> _Lo(mutex);
a++;
}
}
int main()
{
thread t1(fun);
thread t2(fun);
t1.join();
t2.join();
cout << a << endl;
return 0;
}
三、智能指针的使用:
(一)设计一个智能指针类:
- 构造函数,析构函数,operator ,opertor->功能。*
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;
};
(二)库中的一些智能指针:
- 头文件:#include< memoty >
1. auto_ptr: 管理权转移
(1)auto_ptr的问题:当对象拷贝或者赋值后,前面的对象就悬空了,C++98中设计的auto_ptr问题是非常明显的,所以实际中很多公司明确规定了不能使用auto_ptr
(2)管理权转移的思想(模拟实现):
template<class T>
class Auto_ptr
{
Auto_ptr(T* ptr = NULL) :_ptr(ptr)
{}
~Auto_ptr()
{
if (_ptr)
delete _ptr;
}
Auto_ptr(const Auto_ptr<T>& pa) :_ptr(pa._ptr)
{
pa._ptr = NULL;
}
Auto_ptr<T>& operator=(const Auto_ptr<T>& pa)
{
//检测自己是否给自己赋值
if (this != &pa)
{
//释放当前对象的资源
if (_ptr)
_ptr = NULL;
//转移pa中的资源到_ptr(当前对象);
_ptr = pa._ptr;
pa._ptr = NULL;
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
2. unique_ptr:简单粗暴的防拷贝
- 设计思路非常的粗暴-防拷贝,也就是不让拷贝和赋值。
template<class T>
class UniquePtr
{
public:
UniquePtr(T * ptr = nullptr)
: _ptr(ptr)
{}
~UniquePtr()
{
if (_ptr)
delete _ptr;
}
T& operator*() { return *_ptr; }
T* operator->() { return _ptr; }
//禁止拷贝构造和赋值
UniquePtr(UniquePte<T> const &) = delete;
UniquePtr<T> operator=(UniquePtr<T> const &) = delete;
private:
T* _ptr;
};
3. shared_ptr:C++11中提供更靠谱的并且支持拷贝的shared_ptr;
(1)shared_ptr的原理:是通过引用计数的方式来实现多个shared_ptr对象之间共享资源;
(2)模拟实现:
- ①shared_ptr在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共享。
② 在对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减一。
③ 如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源;
④ 如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了。
#include<iostream>
#include<mutex>
using namespace std;
template <class T>
class SharedPtr
{
public:
SharedPtr(T* ptr = nullptr)
: _ptr(ptr)
, _pRefCount(new int(1))
, _pMutex(new mutex)
{}
~SharedPtr() { Release(); }
SharedPtr(const SharedPtr<T>& sp)
: _ptr(sp._ptr)
, _pRefCount(sp._pRefCount)
, _pMutex(sp._pMutex)
{
AddRefCount();
}
// sp1 = sp2
SharedPtr<T>& operator=(const SharedPtr<T>& sp)
{
//if (this != &sp)
if (_ptr != sp._ptr)
{
// 释放管理的旧资源
Release();
// 共享管理新对象的资源,并增加引用计数
_ptr = sp._ptr;
_pRefCount = sp._pRefCount;
_pMutex = sp._pMutex;
AddRefCount();
}
return *this;
}
T& operator*() { return *_ptr; }
T* operator->() { return _ptr; }
int UseCount() { return *_pRefCount; }
T* Get() { return _ptr; }
void AddRefCount()
{
// 加锁或者使用加1的原子操作
_pMutex->lock();
++(*_pRefCount);
_pMutex->unlock();
}
private:
void Release()
{
bool deleteflag = false;
// 引用计数减1,如果减到0,则释放资源
_pMutex.lock();
if (--(*_pRefCount) == 0)
{
delete _ptr;
delete _pRefCount;
deleteflag = true;
}
_pMutex.unlock();
if (deleteflag == true)
delete _pMutex;
}
private:
int* _pRefCount; // 引用计数
T* _ptr; // 指向管理资源的指针
mutex* _pMutex; // 互斥锁
};