C++智能指针读书笔记

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

C++通过一对运算符 new 和 delete 进行动态内存管理,new在动态内存中为对象分配空间并返回一个指向该对象的指针,delete接受一个动态对象的指针,销毁对象并释放与之相关的内存。然而这样的动态内存的使用很危险,因为无法确保始终能在合适的时间释放内存对象。如果忘记释放内存,可能造成内存泄露;如果在尚有指针引用内存的情况下释放内存,会产生非法访问内存的指针。

C++11中,新的标准库提供了两种智能指针(smart pointer)类型来更安全地管理对象。智能指针的使用和常规指针类似,只是它们多了自动释放所指向的对象的功能。两种指针的区别在于管理底层指针的方式:shared_ptr允许多个指针指向同一个对象,unique_ptr不支持。标准库还提供了weak_ptr这一弱指针,指向shared_ptr所管理的对象。三种类型都定义在头文件memory中。

shared_ptr的使用和vector很相似,在尖括号内说明所指向对象的类型:

代码如下:

shared_ptr<string> p1              // p1是shared_ptr,指向string类型
shared_ptr<list<int>> p2     // p2是shared_ptr,指向list的int

解引用一个智能指针就能获得它所指向的对象,在if语句中使用智能指针可以判断它指向的对象是否为空:

代码如下:

// 如果p1非空,检查p1是否指向一个空的string对象
if (p1 && p1->empty())
    *p1 = "creat";        // 如果p1非空且指向一个空的string对象,解引用p1,为其赋新值creat

最安全的分配和使用shared_ptr的方法是调用名为make_shared这一标准库函数。此函数在动态内存中分配并初始化它,返回指向此对象的shared_ptr。该函数定义在memory中。

make_shared的定义和shared_ptr相似,必须制定要创建对象的类型,如:

// 指向一个值为1的int的shared_ptr
shared_ptr<int> p3 = make_shared<int>)(1);
// 指向一个值为“www”的string的shared_ptr
shared_ptr<string> p4 = make_shared<string>(3, "w");
// 指向一个初始化的int,值为0
shared_ptr<int> p5 = make_shared<int>)();

也可以使用auto定义对象保存make_shared,可以省去书写shared_ptr的麻烦。

  shared——ptr中有一个关联的指示器,称为引用计数。可以看做一个计数器,每当shared_ptr对象进行拷贝操作,如用一个shared_ptr对象初始化另一个shared_ptr对象、作为函数的实参、作为函数返回值时,引用计数都会递增(视为数值+1)。当赋予shared_ptr新值或者shared_ptr被销毁时,引用计数递减。当引用计数减为0,通过析构函数,shared_ptr自动销毁所管理的对象,释放内存。

  需要注意的是,如果多个对象共享底层数据,当某一对象被销毁,不能单方面销毁底层数据,例如:

Blob<string> b1;
{ // 新作用域
  Blob<string> b2 = { "x", "b", "b" };
  b1 = b2;
}  // 当离开局部作用域,b2被销毁,然而b2中的元素xbb并不会被销毁
   // b1指向最初由b2创建的元素,即“x”, "b", "b",b1依旧可以它们

  weak_ptr是指向shared_ptr管理的对象的一种智能指针,然而它不控制所指向对象的生存期。将一个weak_ptr绑定在shared_ptr上,不会改变shared_ptr的引用计数,一旦最后一个shared_ptr的指向对象被摧销毁,对象就会被释放,有无weak_ptr并无卵影响。我的理解是,weak_ptr提供了指向shared_ptr底层数据的功能,控制了shared_ptr对底层数据的访问。

  因为weak_ptr指向的对象可能不存在(shared_ptr指向的最后一个对象被销毁时),因而用它不能直接访问对象,必须调用lock函数检查其指向的对象是否存在。很容易写出一个选择语句进行控制:

auto bb = make_shared<string>(2, 'b');
weak_ptr<string> xbb(bb);
if (shared_pr<int> np = xbb.lock()) { // np不为空条件成立
  // 在if语句内,np和xbb共享对象
}

补充weak_ptr相关的函数,便于理解:

w.reset 将w置为空
w.use_count() 与w共享对象的个数
w.expired() 若w.use_count()为0,返回true,否则返回false
w.lock() 若w.expired()为true,返回一个空shared_ptr,否则返回一个指向w的对象的shared_ptr

加入《C++ Primer 5th》中的12.19题参照,题中和“智能指针和异常”并未在本篇随笔中介绍

#include <iostream>    
#include <string>
#include <vector>
#include <memory>
#include <initializer_list>
using namespace std;
using std::string;
using std::vector;
class StrBlobPtr;
class StrBlob {
public:
  friend class StrBlobPtr;      // 友元
  StrBlobPtr begin();         // 声明StrBlob类中的begin()和end()
  StrBlobPtr end();          // 返回一个指向它自身的StrBlobPtr
public:
  typedef vector<string>::size_type size_type;  // 类型别名,size_type = vector<string>::size_type

  StrBlob::StrBlob(initializer_list<string> il) : data(make_shared<vector<string>>(il)) {};  // 接受一个initializer_list参数的构造函数将其参数传                                                            递给对应的vector构造函数,通过拷贝列表
  StrBlob::StrBlob() : data(make_shared<vector<string>>()) {};                 // 构造函数,初始化data成员,指向动态分配的vector                                                            中的值初始化vector元素
  void push_back(const string &t) { data->push_back(t); }
  string& StrBlob::front() {
    check(0, "front on empty StrBlob");
    return data->front();
  }
  string& StrBlob::back() {
    check(0, "back on empty StrBlob");
    return data->back();
  }
  void StrBlob::pop_back() {             // 删除尾元素
    check(0, "pop_back empty StrBlob");
    return data->pop_back();
  }
  string& front() const { return data->front(); };
  string& back() const { return data->back(); };
private:
  shared_ptr<vector<string>> data;
  void StrBlob::check(size_type i, const string &msg) const {  // 检查元素是否存在
    if (i >= data->size())                  // 若不存在
      throw out_of_range(msg);               // 抛出异常
  }
};
class StrBlobPtr {
public:
  StrBlobPtr() : curr(0) {};
  StrBlobPtr(StrBlob &a, size_t sz = 0) : wptr(a.data), curr(sz) {};
  string & deref() const {
    auto p = check(curr, "dereference past end");
    return (*p)[curr];  // check成功,返回一个p指针,指向make_shared指向的vector
  }            // 解引用,make_shared获取vector,用下表运算符返回curr位置上的对象
  StrBlobPtr& incr() {
    check(curr, "increment past end of StrBlobPtr");
    ++curr;
    return *this;
  }
  bool operator!=(const StrBlobPtr& p) { return p.curr != curr; }

private:
  weak_ptr<vector<string>> wptr;
  size_t curr;
  shared_ptr<vector<string>> check(size_t i, const string& msg) const
  {
    auto rent = wptr.lock();
    if (!rent)
      throw runtime_error("unbound StrBlobPtr");
    if (i >= rent->size())
      throw out_of_range(msg);
    return rent;
  }
};
StrBlobPtr StrBlob::begin()
{
  return StrBlobPtr(*this);
}
StrBlobPtr StrBlob::end()
{
  return StrBlobPtr(*this, data->size());
}
(0)

相关推荐

  • C++中的auto_ptr智能指针的作用及使用方法详解

    智能指针(auto_ptr) 这个名字听起来很酷是不是?其实auto_ptr 只是C++标准库提供的一个类模板,它与传统的new/delete控制内存相比有一定优势,但也有其局限.本文总结的8个问题足以涵盖auto_ptr的大部分内容.  auto_ptr是什么? auto_ptr 是C++标准库提供的类模板,auto_ptr对象通过初始化指向由new创建的动态内存,它是这块内存的拥有者,一块内存不能同时被分给两个拥有者.当auto_ptr对象生命周期结束时,其析构函数会将auto_ptr对象拥

  • C++中智能指针如何设计和使用

    智能指针(smart pointer)是存储指向动态分配(堆)对象指针的类,用于生存期控制,能够确保自动正确的销毁动态分配的对象,防止内存泄露.它的一种通用实现技术是使用引用计数(reference count).智能指针类将一个计数器与类指向的对象相关联,引用计数跟踪该类有多少个对象共享同一指针.每次创建类的新对象时,初始化指针并将引用计数置为1:当对象作为另一对象的副本而创建时,拷贝构造函数拷贝指针并增加与之相应的引用计数:对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数(如果

  • 关于c++ 智能指针及 循环引用的问题

    c++智能指针介绍 由于 C++ 语言没有自动内存回收机制,程序员每次 new 出来的内存都要手动 delete,比如流程太复杂,最终导致没有 delete,异常导致程序过早退出,没有执行 delete 的情况并不罕见,并造成内存泄露.如此c++引入 智能指针 ,智能指针即是C++ RAII的一种应用,可用于动态资源管理,资源即对象的管理策略. 智能指针在 <memory>标头文件的 std 命名空间中定义. 它们对 RAII 或 获取资源即初始化 编程惯用法至关重要. RAII 的主要原则是

  • C++中auto_ptr智能指针的用法详解

    智能指针(auto_ptr) 这个名字听起来很酷是不是?其实auto_ptr 只是C++标准库提供的一个类模板,它与传统的new/delete控制内存相比有一定优势,但也有其局限.本文总结的8个问题足以涵盖auto_ptr的大部分内容. auto_ptr是什么? auto_ptr 是C++标准库提供的类模板,auto_ptr对象通过初始化指向由new创建的动态内存,它是这块内存的拥有者,一块内存不能同时被分给两个拥有者.当auto_ptr对象生命周期结束时,其析构函数会将auto_ptr对象拥有

  • 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新特性之智能指针(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++ 中boost::share_ptr智能指针的使用方法

    C++ 中boost::share_ptr智能指针的使用方法 最近项目中使用boost库的智能指针,感觉智能指针还是蛮强大的,在此贴出自己学习过程中编写的测试代码,以供其他想了解boost智能指针的朋友参考,有讲得不正确之处欢迎指出讨论.当然,使用boost智能指针首先要编译boost库,具体方法可以网上查询,在此不再赘述. 智能指针能够使C++的开发简单化,主要是它能够自动管理内存的释放,而且能够做更多的事情,即使用智能指针,则可以再代码中new了之后不用delete,智能指针自己会帮助你管理

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

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

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

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

随机推荐