一、智能指针起因

  在C++中,动态内存的管理是由程序员自己申请和释放的,用一对运算符完成:new和delete。

  new:在动态内存中为对象分配一块空间并返回一个指向该对象的指针;

  delete:指向一个动态独享的指针,销毁对象,并释放与之关联的内存。

  使用堆内存是非常频繁的操作,容易造成堆内存泄露、二次释放等问题,为了更加容易和更加安全的使用动态内存,C++11中引入了智能指针的概念,方便管理堆内存,使得自动、异常安全的对象生存期管理可行。智能指针主要思想是RAII思想,“使用对象管理资源”,在类的构造函数中获取资源,在类的析构函数中释放资源。智能指针的行为类似常规指针,重要的区别是它负责自动释放所指向的对象。

  RAII是Resource Acquisition Is Initialization的简称,即资源获取就是初始化:

  1.定义一个类来封装资源的分配与释放;

  2.构造函数中完成资源的分配及初始化;

  3.析构函数中完成资源的清理,可以保证资源的正确初始化和释放;

  4.如果对象是用声明的方式在栈上创建局部对象,那么RAII机制就会正常工作,当离开作用域对象会自动销毁而调用析构函数释放资源。

二、智能指针类型

  智能指针在C++11版本之后提供,包含在头文件<memory>中,标准命名std空间下,有auto_ptr、shared_ptr、weak_ptr、unique_ptr四种,其中auto_ptr已被弃用。

  auto_ptr:拥有严格对象所有权语义的智能指针;

  shared_ptr:拥有共享对象所有权语义的智能指针;

  weak_ptr:到 shared_ptr 所管理对象的弱引用;

  unique_ptr:拥有独有对象所有权语义的智能指针。

2.1 auto_ptr

  auto_ptr是通过由 new 表达式获得的对象,并在auto_ptr自身被销毁时删除该对象的智能指针,它可用于为动态分配的对象提供异常安全、传递动态分配对象的所有权给函数和从函数返回动态分配的对象,是一个轻量级的智能指针,适合用来管理生命周期比较短或者不会被远距离传递的动态对象,最好是局限于某个函数内部或者是某个类的内部。

  声明:

  template< class T > class auto_ptr;

  template<> class auto_ptr<void>;  // 对类型void特化  

  成员函数:

  (1) get: 获得内部对象的指针;

  (2) release:释放被管理对象的所有权,将内部指针置为空,返回内部对象的指针,此指针需要手动释放;

  (3) reset:销毁内部对象并接受新的对象的所有权;

  (4) operator=:从另一auto_ptr转移所有权;

  (5) operator*operator->:访问被管理对象。

  注意事项:

  (1) 其构造函数被声明为explicit,因此不能使用赋值运算符对其赋值,即不能使用类似这样的形式 auto_ptr<int> p = new int;

  (2) auto_ptr 的对象所有权是独占性的,使用拷贝构造和赋值操作符时,会造成对象所有权的转移,被拷贝对象在拷贝过程中被修改;

  (3) 基于第二条,因此不能将auto_ptr放入到标准容器中或作为容器的成员;

  (4) auto_ptr不能指向数组,释放时无法确定是数组指针还是普通指针;

  (5) 不能把一个原生指针交给两个智能指针对象管理,对其它智能指针也是如此。

  auto_ptr是最早期的智能指针,在C++11 中已被弃用,C++17 中移除,建议使用unique_ptr代替auto_ptr。

  简单实现:

 template<class T>
class AutoPointer
{
public:
AutoPointer(T* ptr)
:mPointer(ptr){} AutoPointer(AutoPointer<T>& other)
{
mPointer= other.mPointer; //管理权进行转移
other.mPointer= NULL;
} AutoPointer& operator = (AutoPointer<T>& other)
{
if(this != &other)
{
delete mPointer;
mPointer = other.mPointer; //管理权进行转移
other.mPointer= NULL;
} return *this;
} ~AutoPointer()
{
delete mPointer;
} T& operator * ()
{
return *mPointer;
} T* operator -> ()
{
return mPointer;
} private: T* mPointer;
};

2.2 shared_ptr

  shared_ptr多个指针指向相同的对象,也叫共享指针。shared_ptr采用了引用计数的方式,更好地解决了赋值与拷贝的问题,每一个shared_ptr的拷贝都指向相同的内存,每拷贝一次内部的引用计数加1,每析构一次内部的引用计数减1,为0时自动删除所指向的堆内存。shared_ptr内部的引用计数是线程安全的,但是对象的读取时需要加锁。

声明:

  template< class T > class shared_ptr; 

  成员函数:

  (1) get: 获得内部对象的指针;

  (2) swap:交换所管理的对象;

  (3) reset:替换所管理的对象;

  (4) use_count:返回shared_ptr所指对象的引用计数;

  (5) operator*operator->:解引用存储的对象指针;

  (6) operator=:对shared_ptr赋值;

  (7) operator bool:检查是否有关联的管理对象;

  (8) owner_before:提供基于拥有者的共享指针排序。

  交换: std::swap(std::shared_ptr) 特化的swap算法用于交换两个智能指针。

  初始化:通过构造函数传入指针初始化,也可以使用std::make_shared 或 std::allocate_shared 函数初始化。

  注意事项:

  (1) 不能将指针直接赋值给一个智能指针,一个是类,一个是指针。不能使用类似这样的形式 shared_ptr<int> p = new int;

  (2) 避免循环引用,这是shared_ptr的一个最大陷阱,导致内存泄漏,这一点在weak_ptr中将得到完善;

  (3) 管理数组指针时,需要制定Deleter以使用delete[]操作符销毁内存,shared_ptr并没有针对数组的特化版本;

  (4) 不能把一个原生指针交给两个智能指针对象管理,对其它智能指针也是如此。

  简单实现:

 template <typename T>
class SharedPointer
{
private: class Implement
{
public:
Implement(T* p) : mPointer(p), mRefs(){}
~Implement(){ delete mPointer;} T* mPointer; //实际指针
size_t mRefs; // 引用计数
}; Implement* mImplPtr; public: explicit SharedPointer(T* p)
: mImplPtr(new Implement(p)){} ~SharedPointer()
{
decrease(); // 计数递减
} SharedPointer(const SharedPointer& other)
: mImplPtr(other.mImplPtr)
{
increase(); // 计数递增
} SharedPointer& operator = (const SharedPointer& other)
{
if(mImplPtr != other.mImplPtr) // 避免自赋值
{
decrease();
mImplPtr = other.mImplPtr;
increase();
} return *this;
} T* operator -> () const
{
return mImplPtr->mPointer;
} T& operator * () const
{
return *(mImplPtr->mPointer);
} private: void decrease()
{
if(--(mImplPtr->mRefs) == )
{
delete mImplPtr;
}
} void increase()
{
++(mImplPtr->mRefs);
}
};

2.3 weak_ptr

  weak_ptr是为了配合shared_ptr而引入的一种智能指针,用于专门解决shared_ptr循环引用的问题,因为它不具有普通指针的行为,没有重载operator * 和 ->,它的最大作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况。weak_ptr可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。weak_ptr可以使用一个非常重要的成员函数lock(),从被观测的shared_ptr获得一个可用的shared_ptr对象,从而操作资源。

  声明:

  template< class T > class weak_ptr; 

  成员函数:

  (1) swap:交换所管理的对象;

  (2) reset:替换所管理的对象;

  (3) use_count:返回shared_ptr所指对象的引用计数;

  (4) operator=:对shared_ptr赋值;

  (5) expired:检查被引用的对象是否已删除;

  (6) owner_before:提供基于拥有者的共享指针排序;

  (7) lock:创建管理被引用的对象的shared_ptr。

  交换:std::swap(std::weak_ptr) 特化的swap算法用于交换两个智能指针。

  注意事项:

  (1) 不能将指针直接赋值给一个智能指针,一个是类,一个是指针。不能使用类似这样的形式 shared_ptr<int> p = new int;

  (2) 不能把一个原生指针交给两个智能指针对象管理,对其它智能指针也是如此。

  简单实现:weak_ptr的典型实现存储二个指针,即指向控制块的指针和作为构造来源的shared_ptr的存储指针。

  以下是VC的源码实现:

 template<class _Ty>
class weak_ptr
: public _Ptr_base<_Ty>
{ // class for pointer to reference counted resource
typedef typename _Ptr_base<_Ty>::_Elem _Elem; public:
weak_ptr()
{ // construct empty weak_ptr object
} template<class _Ty2>
weak_ptr(const shared_ptr<_Ty2>& _Other,
typename enable_if<is_convertible<_Ty2 *, _Ty *>::value,
void *>::type * = )
{ // construct weak_ptr object for resource owned by _Other
this->_Resetw(_Other);
} weak_ptr(const weak_ptr& _Other)
{ // construct weak_ptr object for resource pointed to by _Other
this->_Resetw(_Other);
} template<class _Ty2>
weak_ptr(const weak_ptr<_Ty2>& _Other,
typename enable_if<is_convertible<_Ty2 *, _Ty *>::value,
void *>::type * = )
{ // construct weak_ptr object for resource pointed to by _Other
this->_Resetw(_Other);
} ~weak_ptr()
{ // release resource
this->_Decwref();
} weak_ptr& operator=(const weak_ptr& _Right)
{ // assign from _Right
this->_Resetw(_Right);
return (*this);
} template<class _Ty2>
weak_ptr& operator=(const weak_ptr<_Ty2>& _Right)
{ // assign from _Right
this->_Resetw(_Right);
return (*this);
} template<class _Ty2>
weak_ptr& operator=(shared_ptr<_Ty2>& _Right)
{ // assign from _Right
this->_Resetw(_Right);
return (*this);
} void reset()
{ // release resource, convert to null weak_ptr object
this->_Resetw();
} void swap(weak_ptr& _Other)
{ // swap pointers
this->_Swap(_Other);
} bool expired() const
{ // return true if resource no longer exists
return (this->_Expired());
} shared_ptr<_Ty> lock() const
{ // convert to shared_ptr
return (shared_ptr<_Elem>(*this, false));
}
};

2.4 unique_ptr

  unique_ptr实际上相当于一个安全性增强了的auto_ptr。unique_ptr是通过指针占有并管理另一对象,并在unique_ptr离开作用域时释放该对象的智能指针。unique_ptr的使用标志着控制权的转移,同一时刻只能有一个unique_ptr指向给定对象,通过禁止拷贝语义、只有移动语义来实现。相比与原始指针unique_ptr用于其RAII的特性,使得在出现异常的情况下,动态资源能得到释放。

  声明:

  template< class T, class Deleter = std::default_delete<T> > class unique_ptr;

  template< class T, class Deleter> class unique_ptr<T[], Deleter>;  // 管理数组指针

  成员函数:

  (1) get: 返回指向被管理对象的指针;

  (2) get_deleter:返回用于析构被管理对象7的删除器;

  (3) swap:交换所管理的对象;

  (4) reset:替换所管理的对象;

  (5) release:返回一个指向被管理对象的指针,并释放所有权;

  (6) operator bool:检查是否有关联的被管理对象;

  (7) operator=:为unique_ptr赋值;

  (8) operator*operator->:解引用存储的对象指针。

  注意事项:

  (1) 不能将指针直接赋值给一个智能指针,一个是类,一个是指针。不能使用类似这样的形式 shared_ptr<int> p = new int;

  (2) 不能把一个原生指针交给两个智能指针对象管理,对其它智能指针也是如此。

  简单实现:

 //default deleter for unique_ptr
template<typename T>
struct DefaultDeleter
{
void operator () (T *p)
{
if(p)
{
delete p;
p = NULL;
}
}
}; template<typename T, typename Deleter = DefaultDeleter<T>>
class unique_ptr
{
public: // construct
unique_ptr(T *pT = NULL); // destroy
~unique_ptr(); private: // not allow copyable
unique_ptr(const unique_ptr &); unique_ptr&operator=(const unique_ptr &); public: // reset
void reset(T *p); // release the own of the pointer
T* release(); // get the pointer
T* get(); // convert unique_ptr to bool
operator bool() const; // overload for operator *
T& operator * (); // overload for operator ->
T* operator -> (); private: T *m_pT; //pointer Deleter m_deleter; //deleter void del(); //call deleter
}; template<typename T, typename Deleter>
unique_ptr<T, Deleter>::unique_ptr(T *pT) :m_pT(pT)
{ } template<typename T, typename Deleter>
unique_ptr<T, Deleter>::~unique_ptr()
{
del();
} template<typename T, typename Deleter>
void unique_ptr<T, Deleter>::del()
{
if(*this)
{
m_deleter(m_pT);
m_pT = NULL;
}
} template<typename T, typename Deleter>
T* unique_ptr<T, Deleter>::get()
{
return m_pT;
} template<typename T, typename Deleter>
void unique_ptr<T, Deleter>::reset(T *p)
{
del();
m_pT = p;
} template<typename T, typename Deleter>
T* unique_ptr<T, Deleter>::release()
{
T *p = m_pT;
m_pT = NULL;
return p;
} template<typename T, typename Deleter>
unique_ptr<T, Deleter>::operator bool() const
{
return NULL != m_pT;
} template<typename T, typename Deleter>
T& unique_ptr<T, Deleter>::operator * ()
{
return *m_pT;
} template<typename T, typename Deleter>
T* unique_ptr<T, Deleter>::operator -> ()
{
return m_pT;
}

三、总结

  智能指针就是模拟指针动作的类,一般智能指针都会重载 -> 和 * 操作符。智能指针主要作用是管理动态内存的释放。

  1.不要使用std::auto_ptr;

  2.当你需要一个独占资源所有权的指针,且不允许任何外界访问,请使用std::unique_ptr;

  3.当你需要一个共享资源所有权的指针,请使用std::shared_ptr;

  4.当你需要一个能访问资源,但不控制其生命周期的指针,请使用std::weak_ptr;

  5.不能把一个原生指针交给两个智能指针对象管理。

参考:

https://zh.cppreference.com/w/cpp/memory

https://blog.csdn.net/zhangye3017/article/details/80429780

作者:KeepHopes
出处:https://www.cnblogs.com/yuwanxian/p/11042058.html
关于作者:专注C++,对大数据、人工智能领域颇感兴趣,请多多赐教!
本文为作者原创,版权归作者和博客园共有,转载或引用请注明出处,谢谢!

C++11智能指针原理和实现的更多相关文章

  1. c++11 智能指针 unique_ptr、shared_ptr与weak_ptr

    c++11 智能指针 unique_ptr.shared_ptr与weak_ptr C++11中有unique_ptr.shared_ptr与weak_ptr等智能指针(smart pointer), ...

  2. C++11——智能指针

    1. 介绍 一般一个程序在内存中可以大体划分为三部分——静态内存(局部的static对象.类static数据成员以及所有定义在函数或者类之外的变量).栈内存(保存和定义在函数或者类内部的变量)和动态内 ...

  3. C++11智能指针之std::unique_ptr

    C++11智能指针之std::unique_ptr   uniqut_ptr是一种对资源具有排他性拥有权的智能指针,即一个对象资源只能同时被一个unique_ptr指向. 一.初始化方式 通过new云 ...

  4. 【C++11新特性】 C++11智能指针之weak_ptr

    如题,我们今天要讲的是C++11引入的三种智能指针中的最后一个:weak_ptr.在学习weak_ptr之前最好对shared_ptr有所了解.如果你还不知道shared_ptr是何物,可以看看我的另 ...

  5. 详解C++11智能指针

    前言 C++里面的四个智能指针: auto_ptr, unique_ptr,shared_ptr, weak_ptr 其中后三个是C++11支持,并且第一个已经被C++11弃用. C++11智能指针介 ...

  6. C++11 智能指针

    C++ 11标准库引入了几种智能指针 unique_ptr shared_ptr weak_ptr C++内存管理机制是当一个变量或对象从作用域过期的时候就会从内存中将他干掉.但是如果变量只是一个指针 ...

  7. C++智能指针 原理、使用与实现

    目录 理解智能指针的原理 智能指针的使用 智能指针的设计和实现 1.智能指针的作用 C++程序设计中使用堆内存是非常频繁的操作,堆内存的申请和释放都由程序员自己管理.程序员自己管理堆内存可以提高了程序 ...

  8. C++11智能指针

    今晚跟同学谈了一下智能指针,突然想要看一下C++11的智能指针的实现,因此下了这篇博文. 以下代码出自于VS2012 <memory> template<class _Ty> ...

  9. C++11智能指针的深度理解

    平时习惯使用cocos2d-x的Ref内存模式,回过头来在控制台项目中觉得c++的智能指针有点生疏,于是便重温一下.首先有请c++智能指针们登场: std::auto_ptr.std::unique_ ...

随机推荐

  1. element-ui多重下拉列表的使用

    template <div class="block"> <span class="demonstration">默认 click 触发 ...

  2. vue使用子路由时,默认的子路由视图不显示问题

    解决办法是,将父级的name去掉.(大多数情况下是按name来跳转的,不过这样一改,调到父级就得用路径跳转了): 下面上一下路由的配置: { path: "/index", com ...

  3. $message的问题

    项目中出现$message的问题: 拉取数据成功后 this.$message.success("数据拉取成功")点击拉取第一次不出现,但是代码执行了,后来多次点击就出现了 原因: ...

  4. Selenium定位策略

    1.通过XPath使用contains() 它将启动一个窗口,其中包含文本框开发中涉及的所有特定代码. 记下它的id属性. 通过XPath定位元素的语法 - 使用contains()可以写成: //& ...

  5. zookeeper系列(三)zookeeper的使用--开源客户端

    作者:leesf    掌控之中,才会成功:掌控之外,注定失败, 原创博客地址:http://www.cnblogs.com/leesf456/ 奇文共欣赏,大家共同学习进步. 一.前言 上一篇博客已 ...

  6. linux中 ls |wc -l

      linux中 ls |wc -l_百度知道 答 205行,205个字,1920个字节,wc -l只输出文件行数

  7. ajax-php跨域请求

    php: function __construct(){ // 指定允许其他域名访问 header("Access-Control-Allow-Origin: *"); heade ...

  8. vue问题六:表单验证

    表单验证规则 查看文档:https://blog.csdn.net/weixin_42018790/article/details/80762149 1). el-form增加 :rules=&quo ...

  9. Python:目录

    ylbtech-Python:目录 1.返回顶部   2.返回顶部   3.返回顶部   4.返回顶部   5.返回顶部     6.返回顶部   作者:ylbtech出处:http://ylbtec ...

  10. pyqt5 工具栏文字图片同时显示

    import sys from PyQt5.QtWidgets import QMainWindow, QTextEdit, QAction, QApplication from PyQt5.QtGu ...