目录
一,内存泄露
二,智能指针
智能指针的原理
三,C++库中的智能指针
auto_ptr
unique _ptr
weak_ptr
附:RAII扩展
一,内存泄露
内存泄露,是指因为疏忽或错误造成程序未能释放那些已不在使用的内存的情况;内存泄露并不是内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误失去了对该段内存的控制,因而造成内存的浪费;
内存泄露的危害,长期运行的程序出现内存泄露,影响很大,如操作系统、后台服务器等,出现内存泄露会导致响应越来越慢,最终卡死;
内存泄露的分类
避免内存泄露方式
二,智能指针
RAII(Resource Acquisition Is Initialization),是一种利用对象生命周期来控制程序资源的申请和释放;在对象构造时获取资源,控制对资源的访问使之在对象生命周期内始终保持有效,最后在对象析构的时候释放资源;借此,实际上把管理资源的责任托管给了一个对象;
智能指针,是使用RAII技术的一种封装了指针的类对象,重载了相应的运算符,可像指针一样运算,但会自动回收堆上的内存空间,从而避免内存泄露;
好处:
- 不需要显式地释放资源;
- 对象所需资源在其生命周期内始终有效;
template
class SmartPtr
{
public:
SmartPtr(T* ptr = nullptr)
:_ptr(ptr)
{}
~SmartPtr()
{
if (_ptr)
delete _ptr;
}
private:
T* _ptr;
};
void Func(int* a, int n)
{
int* tmp = (int*)malloc(sizeof(int) * n);
SmartPtr sp(tmp);
}
int main()
{
try
{
int arr[] = { 1,2,3,4,5 };
Func(arr, 5);
}
catch (const exception& e)
{
cout << e.what() << endl;
}
return 0;
}
智能指针的原理
上述SmartPtr还不能将其称为智能指针,因为其不具有指针行为;指针可解引用,也可访问所指空间,因此AutoPtr模板类中还需有*、->重载,才可以让其像指针一样去使用;
template
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 Date
{
int _year;
int _month;
int _day;
};
int main()
{
SmartPtr sp1(new int);
*sp1 = 10;
cout << *sp1 << endl;
SmartPtr sp2(new Date);
(*sp2)._year = 2020; // sp2.operator*()._year;
sp2->_year = 2021; // sp2.operator->()->_year;
return 0;
}
三,C++库中的智能指针
C++库中的智能指针都定义在头文件
auto_ptr
#include
#include
using namespace std;
int main()
{
auto_ptr ap1(new int);
*ap1 = 10;
auto_ptr ap2(ap1);
return 0;
}
template
class AutoPtr
{
public:
AutoPtr(T* ptr = NULL)
:_ptr(ptr)
{}
~AutoPtr()
{
if (_ptr)
delete _ptr;
}
AutoPtr(AutoPtr& ap)
:_ptr(ap._ptr)
{
ap._ptr = NULL;
}
AutoPtr& operator=(AutoPtr& ap)
{
if (this != &ap)
{
if (_ptr)
delete _ptr;
_ptr = ap._ptr;
ap._ptr = NULL;
}
return *this;
}
private:
T* _ptr;
};
unique _ptr
unique_ptr up1(new int);
unique_ptr up2(new int);
//报错,是删除函数,不让拷贝和赋值
unique_ptr cp(up1);
up1 = up2;
template
class UniquePtr
{
public:
UniquePtr(T* ptr = nullptr)
:_ptr(ptr)
{}
~UniquePtr()
{
if (_ptr)
delete _ptr;
}
T& operator*() { return *_ptr };
T& operator->() { return _ptr };
private:
//C++98防拷贝方式,只私有声明不实现
UniquePtr(UniquePtr const&);
UniquePtr& operator=(UniquePtr const&);
//C++11防拷贝方式,delete
UniquePtr(UniquePtr const&) = delete;
UniquePtr& operator=(UniquePtr const&) = delete;
private:
T* _ptr;
};
shared_ptr
auto ptr = new int;
//共享同一块空间,共享所有权
shared_ptr ptr1(ptr);
shared_ptr ptr2(ptr1);
//共享同一块空间,但不共享所有权
shared_ptr ptr1(ptr);
shared_ptr ptr2(ptr);
实现原理,通过引用计数的方式来实现多个shared_ptr对象之间共享资源;
- shared_ptr在其内部,给每个资源都维护着一份计数,用来记录该份资源被几个对象共享;
- 在对象被销毁时(析构),说明该资源不使用了,引用计数减1;
- 如引用计数为0,说明是最后一个使用该资源的对象,必须释放该资源;
- 如引用计数不为0,说明还有对象在使用该资源,不可释放,否则成野指针;
template
class SharedPtr
{
public:
SharedPtr(T* ptr=nullptr)
:_ptr(ptr)
,_pRefCount(new int(1))
,_pMutex(new mutex)
{}
~SharedPtr()
{
Release();
}
SharedPtr(const SharedPtr& sp)
:_ptr(sp._ptr)
, _pRefCount(sp._pRefCount)
, _pMutex(sp._pMutex)
{
AddRefCount();
}
SharedPtr& operator=(const SharedPtr& 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()
{
_pMutex->lock();
++(*_pRefCount);
_pMutex->unlock();
}
private:
void Release()
{
bool deleteflag = false;
_pMutex.lock();
if (--(*_pRefCount == 0))
{
delete _ptr;
delete _pRefCount;
deleteflag = true;
}
if (deleteflag == true)
delete _pMutex;
}
private:
T* _ptr;
int* _pRefCount;
mutex* _pMutex; // 互斥锁
};
shared_ptr线程安全问题
struct listnode
{
~listnode() { cout << "~listnode()" << endl; }
int _data;
shared_ptr _prev;
shared_ptr _next;
};
int main()
{
shared_ptr node1(new listnode);
shared_ptr node2(new listnode);
cout << node1.use_count() << endl;
cout << node2.use_count() << endl;
node1->_next = node2;
node2->_prev = node1;
cout << node1.use_count() << endl;
cout << node2.use_count() << endl;
return 0;
}
weak_ptr
struct listnode
{
~listnode() { cout << "~listnode()" << endl; }
int _data;
weak_ptr _prev;
weak_ptr _next;
};
int main()
{
shared_ptr node1(new listnode);
shared_ptr node2(new listnode);
cout << node1.use_count() << endl;
cout << node2.use_count() << endl;
node1->_next = node2;
node2->_prev = node1;
cout << node1.use_count() << endl;
cout << node2.use_count() << endl;
return 0;
}
如不是new出来的对象,使用删除器;
//仿函数删除器
template
struct FreeFunc
{
void operator()(T* ptr)
{
cout << "free:" << ptr << endl;
free(ptr);
}
};
template
struct DeleteArrayFunc
{
void operator()(T* ptr)
{
cout << "delete[]:" << ptr << endl;
delete[] ptr;
}
};
int main()
{
FreeFunc freefunc;
shared_ptr sp1((int*)malloc(4), freefunc);
DeleteArrayFunc deletearrayfunc;
shared_ptrsp2((int*)malloc(4), deletearrayfunc);
}
C++11和boost中智能指针的关系
- C++98中产生了第一个智能指针auto_ptr;
- C++boost给出了更加实用的scoped_ptr、shared_ptr、weak_ptr;
- C++TR1,引入了shared_ptr等,但TR1不是标准版;
- C++11,引入了unique_ptr、shared_ptr、weak_ptr,uniqu_ptr对应boost的scoped_ptr,且这些智能指针的实现原理参考了boost中的实现;
附:RAII扩展
RAII思想除了可以原来设计智能指针,还可以用来设计守卫锁,防止异常安全导致的死锁问题;
#include
#include
#include
using namespace std;
template
class LockGuard
{
public:
LockGuard(Mutex& mtx)
:_mutex(mtx)
{
_mutex.lock();
}
~LockGuard()
{
_mutex.unlock();
}
LockGuard(const LockGuard&) = delete;
private:
Mutex& _mutex; //必须使用引用
};
mutex mtx;
static int n = 0;
void Func()
{
for (size_t i = 0; i < 1000000; ++i)
{
LockGuard 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;
}