c++11&14-智能指针要点汇总

学c++的人都知道,在c++里面有一个痛点,就是动态内存的管理,就我所经历的一些问题来看,很多莫名其妙的问题,最后都发现是内存管理不当引起的。

但像java等其他一些语言则不会有这样的问题,为什么呢,因为它们有很好的处理内存的方法,比如java的垃圾回收机制,现在,我们c++终于也有了智能指针。

1. 什么是智能指针

简单地说,智能指针是用对象去管理一个资源指针,同时用一个计数器计算引用当前指针对象的个数,当管理指针的对象增加或减少时,计数器也相应加1或减1,当最后一个指针管理对象销毁时,计数器为1,此时在销毁指针管理对象的同时,也对指针管理对象所管理的指针进行delete操作。

下面我们介绍两个常用的智能指针std::shared_ptr和std::weak_ptr。

1.1 std::shared_ptr

std::shared_ptr包装了new操作符动态分配的内存,可以自由拷贝复制,基本上是使用最多的一个智能指针类型。

下面是一个代码例子:

#include <memory>
#include <iostream>
class Test
{
public:
  Test()
  {
    std::cout << "Test()" << std::endl;
  }
  ~Test()
  {
    std::cout << "~Test()" << std::endl;
  }
};
int main()
{
  std::shared_ptr<Test> p1 = std::make_shared<Test>();
  std::cout << "1 ref:" << p1.use_count() << std::endl;
  {
    std::shared_ptr<Test> p2 = p1;
    std::cout << "2 ref:" << p1.use_count() << std::endl;
  }
  std::cout << "3 ref:" << p1.use_count() << std::endl;
  return 0;
}

结果如下:

Test()
1 ref:1
2 ref:2
3 ref:1
~Test()

针对代码解读如下:

std::make_shared里面调用了new操作符分配内存;
第二个p1.use_count()之所以显示为2,是因为增加了引用对象p2,而随着大括号的结束,p2的作用域结束,所以p1的引用计数变回1,而随着main函数的结束,p1的作用域结束,此时检测到计数为1,那就会在销毁p1的同时,调用p1的析构函数delete掉之前分配的内存空间;

1.2 std::weak_ptr

std::weak_ptr有什么特点呢?与std::shared_ptr最大的差别是在赋值的时候,不会引起智能指针计数增加。

weak_ptr被设计为与shared_ptr共同工作,可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。同样,在weak_ptr析构时也不会导致引用计数的减少,它只是一个静静地观察者。weak_ptr没有重载operator*和->,这是特意的,因为它不共享指针,不能操作资源,这是它弱的原因。如要操作资源,则必须使用一个非常重要的成员函数lock()从被观测的shared_ptr获得一个可用的shared_ptr对象,从而操作资源。

1.2.1 std::shared_ptr相互引用会有什么后果

代码如下:

#include <memory>
#include <iostream>
class TestB;
class TestA
{
public:
  TestA()
  {
    std::cout << "TestA()" << std::endl;
  }
  void ReferTestB(std::shared_ptr<TestB> test_ptr)
  {
    m_TestB_Ptr = test_ptr;
  }
  ~TestA()
  {
    std::cout << "~TestA()" << std::endl;
  }
private:
  std::shared_ptr<TestB> m_TestB_Ptr; //TestB的智能指针
};
class TestB
{
public:
  TestB()
  {
    std::cout << "TestB()" << std::endl;
  }
  void ReferTestB(std::shared_ptr<TestA> test_ptr)
  {
    m_TestA_Ptr = test_ptr;
  }
  ~TestB()
  {
    std::cout << "~TestB()" << std::endl;
  }
  std::shared_ptr<TestA> m_TestA_Ptr; //TestA的智能指针
};
int main()
{
  std::shared_ptr<TestA> ptr_a = std::make_shared<TestA>();
  std::shared_ptr<TestB> ptr_b = std::make_shared<TestB>();
  ptr_a->ReferTestB(ptr_b);
  ptr_b->ReferTestB(ptr_a);
  return 0;
}

运行结果:

TestA()
TestB()

可以看到,上面代码中,我们创建了一个TestA和一个TestB的对象,但在整个main函数都运行完后,都没看到两个对象被析构,这是为什么呢?

原来,智能指针ptr_a中引用了ptr_b,同样ptr_b中也引用了ptr_a,在main函数退出前,ptr_a和ptr_b的引用计数均为2,退出main函数后,引用计数均变为1,也就是相互引用。

这等效于说:

ptr_a对ptr_b说,哎,我说ptr_b,我现在的条件是,你先释放我,我才能释放你,这是天生的,造物者决定的,改不了;
ptr_b也对ptr_a说,我的条件也是一样,你先释放我,我才能释放你,怎么办?
是吧,大家都没错,相互引用导致的问题就是释放条件的冲突,最终也可能导致内存泄漏。

1.2.2 std::weak_ptr如何解决相互引用的问题

我们在上面的代码基础上使用std::weak_ptr进行修改,如下:

#include <iostream>
#include <memory>
class TestB;
class TestA
{
public:
  TestA()
  {
    std::cout << "TestA()" << std::endl;
  }
  void ReferTestB(std::shared_ptr<TestB> test_ptr)
  {
    m_TestB_Ptr = test_ptr;
  }
  void TestWork()
  {
    std::cout << "~TestA::TestWork()" << std::endl;
  }
  ~TestA()
  {
    std::cout << "~TestA()" << std::endl;
  }
private:
  std::weak_ptr<TestB> m_TestB_Ptr;
};
class TestB
{
public:
  TestB()
  {
    std::cout << "TestB()" << std::endl;
  }
  void ReferTestB(std::shared_ptr<TestA> test_ptr)
  {
    m_TestA_Ptr = test_ptr;
  }
  void TestWork()
  {
    std::cout << "~TestB::TestWork()" << std::endl;
  }
  ~TestB()
  {
		////把std::weak_ptr类型转换成std::shared_ptr类型
    std::shared_ptr<TestA> tmp = m_TestA_Ptr.lock();
    tmp->TestWork();
    std::cout << "2 ref a:" << tmp.use_count() << std::endl;
    std::cout << "~TestB()" << std::endl;
  }
  std::weak_ptr<TestA> m_TestA_Ptr;
};
int main()
{
  std::shared_ptr<TestA> ptr_a = std::make_shared<TestA>();
  std::shared_ptr<TestB> ptr_b = std::make_shared<TestB>();
  ptr_a->ReferTestB(ptr_b);
  ptr_b->ReferTestB(ptr_a);
  std::cout << "1 ref a:" << ptr_a.use_count() << std::endl;
  std::cout << "1 ref b:" << ptr_a.use_count() << std::endl;
  return 0;
}

运行结果:

TestA()
TestB()
1 ref a:1
1 ref b:1
~TestA::TestWork()
2 ref a:2
~TestB()
~TestA()

预览
由以上代码运行结果我们可以看到:

所有的对象最后都能正常释放,不会存在上一个例子中的内存没有释放的问题;
ptr_aptr_b在main函数中退出前,引用计数均为1,也就是说,在TestATestB中对std::weak_ptr的相互引用,不会导致计数的增加。在TestB析构函数中,调用std::shared_ptr tmp = m_TestA_Ptr.lock(),std::weak_ptr类型转换成std::shared_ptr类型,然后对TestA对象进行调用。

1.2.3 std::weak_ptr支持的调用

weak_ptr<T> w;	//空weak_ptr可以指向类型为T的对象
weak_ptr<T> w(shared_ptr sp);	//与sp指向相同对象的weak_ptr, T必须能转换为sp指向的类型
w = p;	//p可以是shared_ptr或者weak_ptr,赋值后w和p共享对象
w.reset();	//weak_ptr置为空
w.use_count();	//与w共享对象的shared_ptr的计数
w.expired();	//w.use_count()为0则返回true,否则返回false
w.lock();	//w.expired()为true,返回空的shared_ptr;否则返回指向w的shared_ptr

1.3 std::unique_ptr

uniqut_ptr是一种对资源具有排他性拥有权的智能指针,即一个对象资源只能同时被一个unique_ptr指向。

1.3.1 初始化方式

使用new

T *pT = new T();
std::unique_ptr<T> up1(pT);

通过make_unique

auto pT = make_unique<T>();

通过move()函数

//up也是一个std::unique_ptr<T>指针
unique_ptr<T> up1 = std::move(up);

1.3.2 unique_ptr不能被复制或者拷贝

unique_ptr<T> up(new T()); //ok
unique_ptr<T> up1(up); //error, can not be copy
unique_ptr<T> up2 = up; //error, can not be assigned

1.3.3 unique_ptr可以移动赋值或者移动拷贝

unique_ptr<T> pT(new T());
unique_ptr<T> pT2 = std::move(pT);	//移动赋值,此时pT被销毁,为空
unique_ptr<T> pT3(std::move(pt2)); //移动拷贝,此时pT2被销毁,为空

1.3.4 unique_ptr可以作为函数的返回值

unique_ptr<T> GetPtr(); //function getthe unique pointer
unique_ptr<T> pT = GetPtr(); // ok

1.3.5 使用范例

#include <iostream>
int main()
{
	std::unique_ptr<int> pInt;
	pInt.reset(new int());
	int *p = pInt.release(); //释放所有权
	//由于unique_ptr有std::unique_ptr<T[]>的重载函数,所以它可以用来管理数组资源
	std::unique_ptr<int[]> pArray(new int[3]{1,3,3});
}

2. 智能指针小结

可以看出,智能指针其实是std::shared_ptr和std::unique_ptr, std::shared_ptr可以有多个引用对象,但不能互相引用,而std::unique_ptr只能有一个引用,不能赋值或者拷贝,但可以移动赋值和移动拷贝,std::weak_ptr实际上是对std::shared_ptr的补充,它并不能对对象进行具体的操作。

注意:shared_ptr,weak_ptr,unique_ptr这些都是模板,并不是指针或者其他的。

以上就是c++11&14-智能指针要点汇总的详细内容,更多关于c++11&14 智能指针的资料请关注我们其它相关文章!

(0)

相关推荐

  • C++11新特性之智能指针(shared_ptr/unique_ptr/weak_ptr)

    shared_ptr基本用法 shared_ptr采用引用计数的方式管理所指向的对象.当有一个新的shared_ptr指向同一个对象时(复制shared_ptr等),引用计数加1.当shared_ptr离开作用域时,引用计数减1.当引用计数为0时,释放所管理的内存. 这样做的好处在于解放了程序员手动释放内存的压力.之前,为了处理程序中的异常情况,往往需要将指针手动封装到类中,通过析构函数来释放动态分配的内存:现在这一过程就可以交给shared_ptr去做了. 一般我们使用make_shared来

  • C++ 智能指针的模拟实现实例

    C++ 智能指针的模拟实现实例 1.引入 int main() { int *p = new int; //裸指针 delete p; return 0; } 在上面的代码中定义了一个裸指针p,需要我们手动释放.如果我们一不小心忘记释放这个指针或者在释放这个指针之前,发生一些异常,会造成严重的后果(内存泄露).而智能指针也致力于解决这种问题,使程序员专注于指针的使用而把内存管理交给智能指针. 普通指针也容易出现指针悬挂问题,当有多个指针指向同一个对象的时候,如果某一个指针delete了这个对象,

  • C++ 智能指针深入解析

    1. 为什么需要智能指针?简单的说,智能指针是为了实现类似于Java中的垃圾回收机制.Java的垃圾回收机制使程序员从繁杂的内存管理任务中彻底的解脱出来,在申请使用一块内存区域之后,无需去关注应该何时何地释放内存,Java将会自动帮助回收.但是出于效率和其他原因(可能C++设计者不屑于这种傻瓜氏的编程方式),C++本身并没有这样的功能,其繁杂且易出错的内存管理也一直为广大程序员所诟病. 更进一步地说,智能指针的出现是为了满足管理类中指针成员的需要.包含指针成员的类需要特别注意复制控制和赋值操作,

  • C++智能指针读书笔记

    最近在补看<C++ Primer Plus>第六版,这的确是本好书,其中关于智能指针的章节解析的非常清晰,一解我以前的多处困惑.C++面试过程中,很多面试官都喜欢问智能指针相关的问题,比如你知道哪些智能指针?shared_ptr的设计原理是什么?如果让你自己设计一个智能指针,你如何完成?等等--.而且在看开源的C++项目时,也能随处看到智能指针的影子.这说明智能指针不仅是面试官爱问的题材,更是非常有实用价值. C++通过一对运算符 new 和 delete 进行动态内存管理,new在动态内存中

  • C++智能指针实例详解

    本文通过实例详细阐述了C++关于智能指针的概念及用法,有助于读者加深对智能指针的理解.详情如下: 一.简介 由于 C++ 语言没有自动内存回收机制,程序员每次 new 出来的内存都要手动 delete.程序员忘记 delete,流程太复杂,最终导致没有 delete,异常导致程序过早退出,没有执行 delete 的情况并不罕见. 用智能指针便可以有效缓解这类问题,本文主要讲解参见的智能指针的用法.包括:std::auto_ptr.boost::scoped_ptr.boost::shared_p

  • C++智能指针shared_ptr分析

    C++智能指针shared_ptr分析 概要: shared_ptr是c++智能指针中适用场景多,功能实现较多的智能指针.它采取引用计数的方法来实现释放指针所指向的资源.下面是我代码实现的基本功能. 实例代码: template<class T> class sharedptr { public: sharedptr(T* ptr) :_ptr(ptr) , _refCount(new int(1)) {} sharedptr(sharedptr<T>& sp) :_ptr

  • c++11&14-智能指针要点汇总

    学c++的人都知道,在c++里面有一个痛点,就是动态内存的管理,就我所经历的一些问题来看,很多莫名其妙的问题,最后都发现是内存管理不当引起的. 但像java等其他一些语言则不会有这样的问题,为什么呢,因为它们有很好的处理内存的方法,比如java的垃圾回收机制,现在,我们c++终于也有了智能指针. 1. 什么是智能指针 简单地说,智能指针是用对象去管理一个资源指针,同时用一个计数器计算引用当前指针对象的个数,当管理指针的对象增加或减少时,计数器也相应加1或减1,当最后一个指针管理对象销毁时,计数器

  • C++11智能指针unique_ptr用法使用场景分析

    一.概述 C++ 标准模板库 STL(Standard Template Library) 一共给我们提供了四种智能指针:auto_ptr.unique_ptr.shared_ptr 和 weak_ptr,其中 auto_ptr 是 C++98 提出的,C++11 已将其摒弃,并提出了 unique_ptr 替代 auto_ptr.虽然 auto_ptr 已被摒弃,但在实际项目中仍可使用,但建议使用更加安全的 unique_ptr,后文会详细叙述.shared_ptr 和 weak_ptr 则是

  • C++11 智能指针之shared_ptr代码详解

    C++中的智能指针首先出现在"准"标准库boost中. 随着使用的人越来越多,为了让开发人员更方便.更安全的使用动态内存,C++11也引入了智能指针来管理动态对象. 在新标准中,主要提供了shared_ptr.unique_ptr.weak_ptr三种不同类型的智能指针. 接下来的几篇文章,我们就来总结一下这些智能指针的使用. 今天,我们先来看看shared_ptr智能指针. shared_ptr 智能指针 shared_ptr是一个引用计数智能指针,用于共享对象的所有权也就是说它允许

  • C++11智能指针之weak_ptr详解

    如题,我们今天要讲的是 C++11 引入的三种智能指针中的:weak_ptr. 在学习 weak_ptr 之前最好对 shared_ptr 有所了解.如果你还不知道 shared_ptr 是何物,可以看看另一篇文章: [C++11新特性] C++11智能指针之shared_ptr 1.为什么需要weak_ptr? 在正式介绍weak_ptr之前,我们先来回忆一下shared_ptr的一些知识. 我们知道shared_ptr是采用引用计数的智能指针,多个shared_ptr实例可以指向同一个动态对

  • C++11智能指针中的 unique_ptr实例详解

    在前面一篇文章中,我们了解了 C++11 中引入的智能指针之一 shared_ptr 和 weak_ptr ,今天,我们来介绍一下另一种智能指针 unique_ptr . 往期文章参考: [C++11新特性] C++11 智能指针之shared_ptr [C++11新特性] C++11智能指针之weak_ptr unique_ptr介绍 unique是独特的.唯一的意思,故名思议,unique_ptr可以"独占"地拥有它所指向的对象,它提供一种严格意义上的所有权. 这一点和我们前面介绍

  • c++11&14-多线程要点汇总

    在C++11以前,C++的多线程编程均需依赖系统或第三方接口实现,一定程度上影响了代码的移植性.C++11中,引入了boost库中的多线程部分内容,形成C++标准,形成标准后的boost多线程编程部分接口基本没有变化,这样方便了以前使用boost接口开发的使用者切换使用C++标准接口,很容易把boost接口升级为C++标准接口. 我们通过如下几部分介绍C++11多线程方面的接口及使用方法. 1. std::thread std::thread为C++11的线程类,使用方法和boost接口一样,非

  • c++11&14-STL要点汇总

    在c++里面不得不提的一个标准库,就是STL,STL包含很多实用的数据结构,如vector,list,map,set等都是我们常用的,而c++11也对STL做了一些补充,使得STL的内容越来越丰富,可选择的也越来越多了. 1. std::array 先看一段代码: #include <array> #include <iostream> int main() { std::array<int, 4> arrayDemo = { 1,2,3,4 }; std::cout

  • c++11 新特性——智能指针使用详解

    c++11添加了新的智能指针,unique_ptr.shared_ptr和weak_ptr,同时也将auto_ptr置为废弃(deprecated). 但是在实际的使用过程中,很多人都会有这样的问题: 不知道三种智能指针的具体使用场景 无脑只使用shared_ptr 认为应该禁用raw pointer(裸指针,即Widget*这种形式),全部使用智能指针 初始化方法 class A { public: A(int size){ this->size = size; } A(){} void Sh

  • C++11 智能指针的具体使用

    目录 智能指针的原理 RAII 智能指针的原理 auto_ptr 1.auto_ptr的使用及问题 unique_ptr shared_ptr shared_ptr的循环引用 智能指针的原理 RAII RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存.文件句柄.网络连接.互斥量等等)的简单技术. 在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源. 借此,我

  • C++11中的智能指针shared_ptr、weak_ptr源码解析

    目录 1.前言 2.源码准备 3.智能指针概念 4.源码解析 4.1.shared_ptr解析 4.1.1.shared_ptr 4.1.2.__shared_ptr 4.1.3.__shared_count 4.1.4._Sp_counted_base 4.1.5._Sp_counted_ptr 4.1.6.shared_ptr总结 4.2.weak_ptr解析 4.2.1.weak_ptr 4.2.2.__weak_ptr 4.2.3.__weak_count 4.2.4.回过头看weak_

随机推荐