1.为什么需要智能指针?
当我们写一个new语句时,一般就会立即把delete语句直接也写了,但是我们不能避免程序还未执行到delete时就跳转了或者在函数中没有执行到最后的delete语句就返回了,如果我们不在每一个可能跳转或者返回的语句前释放资源,就会造成内存泄露。使用智能指针可以很大程度上的避免这个问题,因为智能指针就是一个类,当超出了类的作用域是,类会自动调用析构函数,析构函数会自动释放资源。
2.什么是智能指针?
智能指针是一种基于RAII思想的特殊的类,它封装了一个指针,重载了->,*运算符,实现了C++内存自动回收机制。
RAII: 是一种利用对象生命周期来控制程序资源(如内存、文件句 柄、网络连接、互斥量等等)的简单技术。
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的 时候释放资源。这种做法使得不需要显示的释放资源、对象所需资源在其生命周期内都有效。
2.1 std::auto_ptr
先看如下demo:
#include<memory>
class A {
public:
A() { std::cout << "A()" << std::endl; }
~A() { std::cout << "~A()" << std::endl; }
int m;
};
int main()
{
std::auto_ptr<A> a(new A);
std::auto_ptr<A> cpoy(a);
a->m = 100;
return 0;
}
执行代码我们可以看到,输出“A()”后程序会崩溃,这就是auto_ptr的缺陷:当对象拷贝或者赋值后,前面的对象就悬空了,从而执行a->m时解引用失败。
2.1.1模拟实现auto_ptr
#include<iostream>
template<class T>
class Auto_ptr{
public:
Auto_ptr(T* ptr = nullptr):_ptr(ptr){}
~Auto_ptr(){if(_ptr) delete _ptr;}
Auto_ptr(Auto_ptr<T> &ap)
:_ptr(ap._ptr)
{
ap._ptr = nullptr;
}
Auto_ptr<T>& operator=(Auto_ptr<T> &ap){
if(this != ap){
if(_ptr)
delete _ptr;
_ptr = ap._ptr;
ap._ptr = nullptr;
}
return *this;
}
T& operator*(){
return *_ptr;
}
T& operator->(){
return _ptr;
}
private:
T *_ptr;
};
从实现原理来看,当拷贝或者赋值时,会销毁当前对象ap,所以会出现拷贝或赋值之后,前面的对象就会悬空。
2.2 std::unique_ptr
#include<memory>
class a {
public:
a() { std::cout << "a()" << std::endl; }
~a() { std::cout << "~a()" << std::endl; }
int m;
};
int main()
{
std::unique_ptr<a> up(new a);
std::unique_ptr<a> copy(up);
std::unique_ptr<a> ap(new a);
ap = up;
return 0;
}
执行上面的程序,会发现编译不通过,并且报错“调用已删除的函数”,这是c++11的防拷贝方式。这也印证了unique_ptr的特点,直接私有拷贝构造和赋值函数,防止拷贝或者赋值。
2.2.1 模拟实现unique_ptr
#include<iostream>
template<class T>
class UniquePtr{
UniquePtr(T* ptr = nullptr)
:_ptr(ptr){}
~UniquePtr(){if(_ptr) delete _ptr;}
T& operator*(){return *_ptr;}
T* operator->(){return _ptr;}
private:
UniquePtr(UniquePtr<T>const &) = delete;
UniquePtr& operator=(UniquePtr<T>const &) = delete;
private:
T *_ptr;
};
2.3 std::shared_ptr
share_ptr的使用方式如下:
int main()
{
std::shared_ptr<a> sp(new a);
std::shared_ptr<a> copy(sp);
std::cout << "use_count:" << sp.use_count() << std::endl;
std::cout << "use_count:" << copy.use_count() << std::endl;
return 0;
}
可以看到,share_ptr是通过引用计数的方式来实现多个shared_ptr对象之间共享资源。
2.3.1 模拟实现shared_ptr
shared_ptr原理:
- shared_ptr在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共享。
- 在对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减一。
- 如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源。
- 如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指 针了。
#include<mutex>
#include<thread>
#include <iostream>
template<class T>
class SharedPtr{
public:
SharedPtr(T* ptr = nullptr)
:_ptr(ptr)
,_count(new int(1))
,_mutex(new std::mutex)
{}
SharedPtr(const SharedPtr<T>& sp)
:_ptr(sp._ptr)
,_count(sp._count)
,_mutex(sp._mutex)
{
AddCount();
}
SharedPtr<T>& operator=(const SharedPtr<T>& sp){
if(_ptr != sp._ptr){
Release();
_ptr = sp._ptr;
_count = sp._count;
_mutex = sp._mutex;
AddCount();
}
return *this;
}
~SharedPtr(){
Release();
}
T& operator*(){return *_ptr;}
T& operator->(){return _ptr;}
int usecount(){return *_count;}
T* Get(){return _ptr;}
private:
void AddCount(){
_mutex->lock();
(*_count)++;
_mutex->unlock();
}
void Release(){
bool deletefg = false;
_mutex->lock();
if(--(*_count) == 0){
delete _ptr;
delete _count;
deletefg = true;
}
_mutex->unlock();
if(deletefg == true)
delete _mutex;
}
private:
T* _ptr;
int* _count;
std::mutex* _mutex;
};