C++11 智能指针
这是我在项目中深刻理解智能指针的总结,希望对你有帮助。

引言
其实boost库早在C++11之前很久就开始提出了智能指针的概念,那么智能指针是什么,为什么会有智能指针,现在的指针还不够吗,还要专门提出一种智能指针的思想。,
指针可以说C/C++的基础,我们可以通过指针直接访问内存,准确的说是虚拟内存,指针是C/C++这套编程语言的根基,但是指针虽然特别好,但是这是一个双刃剑,有一个非常致命的问题,那就是C/C++中我们可以通过malloc free new delete来实现从堆中创建对象,释放对象,就是这个操作,导致了C/C++中内存隐患的问题,包括如今提出的Rust就是为了解决这个问题。
C++内存隐患
C++中我们在堆上创建了一个对象后,必须手动的释放,也就是说,malloc之后必须delete ,不然会造成什么问题,这个问题就是内存泄漏,我们创建了一个空间,但是没有释放,操作系统认为我们在使用这个空间,但是实际上我们并没有利用,这样就导致这块空间实际上就是没有利用,占用了资源。
为什么容易造成内存泄露
-
- throw try catch机制:C++在抛出异常的时候,会直接弹出,我们难道在每个throw的位置都加上delete吗?可以是可以,但是代码太冗余了。
void func(int val) { int* p = new int(); if(val == 0) { delete p; throw error(); } delete p; } -
- 多线程环境:在多线程,多个执行流(线程)访问同一个资源,那么资源的回收时非常复杂的,如果判断没有线程使用它,来进行资源的回收,这也是一个问题。
智能指针
为了解决上述的问题,C++11提出了智能指针的设计,谈到智能指针,绕不开的就是RAII的思想、
RAII的思想
RAII(Resource Acquisition Is Initialization)是由c++之父Bjarne Stroustrup提出的,中文翻译为资源获取即初始化,他说:使用局部对象来管理资源的技术称为资源获取即初始化;这里的资源主要是指操作系统中有限的东西如内存、网络套接字等等,局部对象是指存储在栈的对象,它的生命周期是由操作系统来管理的,无需人工介入;
不愧是C++的设计者,这个思想真的很厉害,RAII通俗的理解方式是什么呢?就是创建一个类来管理对应元素的生命周期。
比如智能指针的构想就是,设计一个类来管理指针,通过运算符的重载,他支持指针的所有操作类型,通过在构造的时候,他通过构造函数创建一个指针,在析构的时候释放指针。
这样函数栈帧结束的时候,他就会调用类的析构函数,实现指针的释放,统一资源的回收。
auto_ptr
第一版的智能指针,存在很多的问题,现在已经几乎不再使用,但是他是先驱。

看看他的接口

重载了指针的各种操作。
但是在实际开发中我们发现了auto_ptr的局限性,所以针对智能指针进行功能的分类,具体分为shared_ptr, unique_ptr, weak_ptr
unique_ptr
unique的意思是独特的,这个指针表明,他只能够创建一次,不支持拷贝和赋值重载,他是独特的,独有的,但是移动构造显然是支持的。
创建他的方式
std::unique_ptr<int> pi(new int()); // C++11
std::unique_ptr<int> pi = std::make_unique<int>(); // C++14
模拟实现
#ifndef __UNIQUE_PTR__
#define __UNIQUE_PTR__
#include <iostream>
namespace mycode
{
template <class PtrType>
struct Deltor
{
void operator()(PtrType *ptr)
{
delete ptr;
}
};
template <class PtrType>
struct Deltor<PtrType[]>
{
void operator()(PtrType *ptr)
{
delete[] ptr;
}
};
template <class PtrType, class Del = Deltor<PtrType>>
class unique_ptr
{
public:
unique_ptr(PtrType *ptr) : _ptr(ptr) {}
using Ref = PtrType &;
using Ptr = PtrType *;
using self = unique_ptr<PtrType, Del>;
Ref operator*() { return *_ptr; }
Ptr operator->() { return _ptr; }
operator PtrType *() { return _ptr; }
~unique_ptr()
{
_deltor(_ptr);
}
// 禁止拷贝构造和拷贝赋值
unique_ptr(const self &) = delete;
unique_ptr &operator=(const self &) = delete;
// 允许移动构造和移动赋值
unique_ptr(self &&other) noexcept : _ptr(other._ptr)
{
other._ptr = nullptr;
}
self &operator=(self &&other) noexcept
{
if (this != &other)
{
_deltor(_ptr);
_ptr = other._ptr;
other._ptr = nullptr;
}
return *this;
}
private:
PtrType *_ptr;
Del _deltor; // 用于释放指针
};
template <class PtrType, class Del>
class unique_ptr<PtrType[], Del>
{
public:
unique_ptr(PtrType *ptr) : _ptr(ptr) {}
using Ref = PtrType &;
using Ptr = PtrType *;
using self = unique_ptr<PtrType[], Del>;
Ref operator*() { return *_ptr; }
Ptr operator->() { return _ptr; }
operator PtrType *() { return _ptr; }
~unique_ptr()
{
_deltor(_ptr);
}
// 禁止拷贝构造和拷贝赋值
unique_ptr(const self &) = delete;
unique_ptr &operator=(const self &) = delete;
// 允许移动构造和移动赋值
unique_ptr(self &&other) noexcept : _ptr(other._ptr)
{
other._ptr = nullptr;
}
self &operator=(self &&other) noexcept
{
if (this != &other)
{
_deltor(_ptr);
_ptr = other._ptr;
other._ptr = nullptr;
}
return *this;
}
private:
PtrType *_ptr;
Del _deltor; // 用于释放指针
};
}
#endif
shared_ptr
有了unique_ptr当然还有shared, 可以分享的支持,我可以拷贝你,也就是我可以传值传参。
怎么实现呢?、

引用计数
我们在类中添加一个计数器,当调用拷贝构造和赋值重载的时候我们计数器++,析构的时候,计数器–,如果减到0,那么我们就调用析构函数,释放对象。
具体实现
shared_ptr
#include <iostream>
#ifndef __SHARED_PTR__
#define __SHARED_PTR__
#include "unique_ptr.hpp"
namespace mycode
{
template <class PtrType, class Del = Deltor<PtrType>>
class shared_ptr
{
public:
shared_ptr(PtrType *ptr) : _ptr(ptr), _pcount(new int(1))
{
}
using Ref = PtrType &;
using Ptr = PtrType *;
using self = shared_ptr<PtrType, Del>;
Ref operator*() { return *_ptr; }
Ptr operator->() { return _ptr; }
operator PtrType *() { return _ptr; }
~shared_ptr()
{
if ((*_pcount) > 1)
(*_pcount)--;
else
{
_deltor(_ptr);
delete _pcount;
_pcount = nullptr;
}
}
// 禁止拷贝构造和拷贝赋值
shared_ptr(const self &other) : _ptr(other._ptr)
{
(*_pcount)++;
}
self &operator=(const self &other)
{
if (this == &other)
return *this;
this->~shared_ptr(); // 将自身本身进行析构
_ptr = other._ptr;
(*_pcount)++;
return *this;
}
// 允许移动构造和移动赋值
shared_ptr(self &&other) noexcept : _ptr(other._ptr)
{
(*_pcount)++;
}
self &operator=(self &&other) noexcept
{
if (this == &other)
return *this;
this->~shared_ptr(); // 将自身本身进行析构
_ptr = other._ptr;
(*_pcount)++;
return *this;
}
private:
Ptr _ptr;
int *_pcount = nullptr;
Del _deltor; // 用于释放指针
};
template <class PtrType, class Del>
class shared_ptr<PtrType[],Del>
{
public:
shared_ptr(PtrType *ptr) : _ptr(ptr), _pcount(new int(1))
{
}
using Ref = PtrType &;
using Ptr = PtrType *;
using self = shared_ptr<PtrType, Del>;
Ref operator*() { return *_ptr; }
Ptr operator->() { return _ptr; }
operator PtrType *() { return _ptr; }
~shared_ptr()
{
if ((*_pcount) > 1)
(*_pcount)--;
else
{
_deltor(_ptr);
delete _pcount;
_pcount = nullptr;
}
}
// 禁止拷贝构造和拷贝赋值
shared_ptr(const self &other) : _ptr(other._ptr)
{
(*_pcount)++;
}
self &operator=(const self &other)
{
if (this == &other)
return *this;
this->~shared_ptr(); // 将自身本身进行析构
_ptr = other._ptr;
(*_pcount)++;
return *this;
}
// 允许移动构造和移动赋值
shared_ptr(self &&other) noexcept : _ptr(other._ptr)
{
(*_pcount)++;
}
self &operator=(self &&other) noexcept
{
if (this == &other)
return *this;
this->~shared_ptr(); // 将自身本身进行析构
_ptr = other._ptr;
(*_pcount)++;
return *this;
}
private:
Ptr _ptr;
int *_pcount = nullptr;
Del _deltor; // 用于释放指针
};
// template <class PtrType, class Del>
// class unique_ptr<PtrType[], Del>
// {
// public:
// unique_ptr(PtrType *ptr) : _ptr(ptr) {}
// using Ref = PtrType &;
// using Ptr = PtrType *;
// using self = unique_ptr<PtrType[], Del>;
// Ref operator*() { return *_ptr; }
// Ptr operator->() { return _ptr; }
// operator PtrType *() { return _ptr; }
// ~unique_ptr()
// {
// _deltor(_ptr);
// }
// // 禁止拷贝构造和拷贝赋值
// unique_ptr(const self &) = delete;
// unique_ptr &operator=(const self &) = delete;
// // 允许移动构造和移动赋值
// unique_ptr(self &&other) noexcept : _ptr(other._ptr)
// {
// other._ptr = nullptr;
// }
// self &operator=(self &&other) noexcept
// {
// if (this != &other)
// {
// _deltor(_ptr);
// _ptr = other._ptr;
// other._ptr = nullptr;
// }
// return *this;
// }
// private:
// PtrType *_ptr;
// Del _deltor; // 用于释放指针
// };
}
namespace StandardModule
{
template <class T,class Del>
using shared_ptr = mycode::shared_ptr<T,Del>;
#endif
weak_ptr
weak虚弱的,虚弱的指针,我们一般叫做弱指针,他并没有管理指针。
感觉unique_ptr shared_ptr已经够了为什么还要弱指针呢?他的存在是为了解决shared_ptr的坑。
shared_ptr的坑
最经典的坑就是你中有我,我中有你的坑?
情景:
我有一个公司的类,还有员工的类,公司需要管理员工,所以公司中有员工的指针,但是员工可以通过公司辞职,员工也有公司的指针,如果这两种指针都用shared_ptr, 那么恭喜你,中奖了,重了智能指针最经典的大坑。

如果我们进行释放公司的时候,会发生什么?
释放公司的时候,因为员工还没有释放,所以公司的指针有两份(员工和自己), 所以公司不释放,员工释放的时候,因为公司还没有释放,公司拥有员工指针,所以,员工也有两个人持有,不释放,最终的结果就是谁都不释放,内存泄漏。
那么为什么会出现这个问题呢?
关键就是员工拥有公司指针这个问题上?员工并没有拥有公司,他只有拥有访问公司的机会,但是shared_ptr让他直接拥有了公司,所以, weak_ptr诞生了,weak_ptr不含有指针,但是拥有访问对象的机会,相当于提供了一个访问公司的接口。
这样不就解决了吗?员工中的公司使用weak_ptr, 公司含有员工使用shared_ptr。

从接口可以看出,weak_ptr最关键的接口就是expired, weak_ptr通过shared_ptr进行初始化,他的内部存储了访问这个shared_ptr的通道,当我们想要访问shared_ptr的时候,我们可以调用expired这个接口返回一个shared_ptr, 但是我们并不会==拥有公司,我们只有需要的时候访问公司的权利==。
deleter

智能指针能支持我们自定义析构器,比如我们自己写了一个内存池,我们希望的不是默认的delete , 而是我们自己内存池的dealloc, 这个时候就可以使用这个。
总结
这是我的一家之谈,当然也是看了一些别人观点的启发,如果有错误,希望指正,如果对你理解智能指针有帮助,那真的太棒了。

1133

被折叠的 条评论
为什么被折叠?



