美文网首页
第 12 章 动态内存(1)

第 12 章 动态内存(1)

作者: cb_guo | 来源:发表于2019-04-21 21:44 被阅读0次
  • 全局对象在程序启动时分配,在程序结束时销毁
  • 对于局部自动对象,当我们进入其定义所在的程序块时被创建,在离开时销毁
  • 局部 static 对象在第一次使用前分配,在程序结束时销毁

12.1 动态内存与智能指针

  • 在C++中,动态内存的管理是通过一对运算符来完成的:new,在动态内存中为对象分配空间并返回一个指向该对象的指针,我们可以选择对对象进行初始化。delete,接受一个动态对象的指针,销毁该对象,并释放与之关联的内存。
  • 动态内存的使用很容易出现问题,因为确保在正确的时间释放内存是及其困难的。有时我们会忘记释放内存,在这种情况下会产生内存泄露;有时在尚有指针引用内存的情况下我们就释放了它,在这种情况下就会产生引用非法内存的指针。
  • 为了更容易(同时也更安全)地使用动态内存,新的标准库提供了两种智能指针类型来管理动态对象。智能指针的行为类似常规指针,重要的区别是它负责自动释放所指向的对象。新标准库提供的这两种智能指针的区别在于管理底层指针的方式;shared_ptr 允许多个指针指向同一个对象;unique_ptr 则'独占'所指向的对象。标准库还定义了一个名为 weak_ptr 的伴随类,它是一种弱引用,指向 shared_ptr 所管理的对象。这三种类型都定义在 memory 头文件中
12.1.1 shared_ptr 类
shared_ptr 和 unique_ptr 都支持的操作
shared_ptr<T> 空指针,可以指向类型为 T 的对象
unique_ptr<T>

p             将 p 作为一个条件判断,若 p 指向一个对象,则为 true

*p            解引用 p ,获得它指向的对象

p->mem        等价于 (*p).mem

p.get()       返回 p 中保存的指针。要小心使用,若智能指针释放了其对象,返回的指针所指向的对象也就消失了

swap(p, q)    交换 p 和 q 中的指针
p.swap(q)
shared_ptr 独有操作
make_shared<T>(args)  返回一个 shared_ptr,指向一个动态分配的类型为 T 的对象。使用 args 初始化此对象

shared_ptr<T>p(q)     p 是 shared_ptr q 的拷贝;此操作会递增 q 中的计数器。q中的指针必须能转换成T*

p = q                 p 和 q 都是 shared_ptr ,所保存的指针必须能相互转换。
                      此操作会递减 p 的引用计数,递增 q 的引用计数;
                      若 p 的引用计数为 0 ,则将其管理的原内存释放

p.unique()            若 p.use_count() 为 1,返回 true,否则返回 false

p.use_count()         返回与 p 共享对象的智能指针数量;可能很慢,主要用于调试
make_shared 函数

最安全的分配和使用动态内存的方法是调用一个名为 make_shared 的标准库函数。此函数在动态内存中分配一个对象并初始化它,返回指向此对象的 shared_ptr 。与智能指针一样,make_shared 也定义在头文件 memory 中

// 指向一个值为42 的 int 的 shared_ptr
shared_ptr<int> p3 = make_shared<int>(42);

// p4 指向一个值为 "9999999999" 的 string
shared_ptr<string> p4 = make_shared<string>(10,'9');

// p5 指向一个值初始化的 int ,即值为 0 
shared_ptr<int> p5 = make_shared<int>();

// 当然,我们通常用 auto 定义一个对象来保存 make_shared 的结果,这种方式比较简单
// p6 指向一个动态分配的空 vector<string>
auto p6 = make_shared<vector<string>>();
shared_ptr 的拷贝和赋值
  • 当进行拷贝或赋值操作时,每个 shared_ptr 都会记录有多少个其他 shared_ptr 指向相同的对象
auto p = make_shared<int>(42);   p 指向的对象只有 p 一个引用者
quto q(p);                       p q 指向相同的对象,此对象有两个引用者
  • 我们可以认为每个 shared_ptr 都有一个关联的计数器,通常称其为引用计数。无论何时我们拷贝一个 shared_ptr,计数器都会递增。
  • 例如,当用一个 shared_ptr 初始化另一个 shared_ptr,或将它作为参数传递给一个函数以及作为一个函数的返回值时,它所关联的计数器都会递增
  • 当我们给 shared_ptr 赋予一个新值或是 shared_ptr 被销毁时(例如一个局部的 shared_ptr 离开其作用域)时,计数器就会递减
  • 一旦一个 shared_ptr 的计数器变为 0,它就会自动释放自己所管理的对象
auto r = make_shared<int>(42);  r 指向的 int 只有一个引用者
r = q;    给 r 赋值,令它指向另一个地址
          递增 q 指向的对象的引用计数
          递减 r 原来指向的对象的引用计数
          r 原来指向的对象已没有引用者,会自动释放
shared_ptr 自动销毁所管理的对象
  • 当指向一个对象的最后一个 shared_ptr 被销毁时,shared_ptr 类会自动销毁此对象。它通过另一个特殊的成员函数 - 析构函数来完成销毁操作
  • shared_ptr 的析构函数会递减它所指向的对象引用计数。如果引用计数变为 0,shared_ptr 的析构函数就会销毁对象,并释放它占用的内存
shared_ptr 还会自动释放相关联的内存
  • 当动态对象不再被使用时,shared_ptr 类会自动释放动态对象,这一特性使得动态内存的使用变得非常容易。例如,我们可能有一个函数,它返回一个 shared_ptr ,指向一个 Foo 类型的动态分配的对象,对象是通过一个类型为 T 的参数进行初始化的:
// factory 返回一个 shared_ptr ,指向一个动态分配的对象
shared_ptr<Foo> factory(T arg){
     // 恰当处理 arg
     // shared_ptr 负责释放内存
     return make_shared<Foo>(arg);
}
  • 由于 factory 返回一个 shared_ptr ,所以我们可以确保它分配的对象会在恰当的时刻被释放。例如,下面的函数将 factory 返回的 shared_ptr 保存在局部变量中:
void use_factory(T arg){
    shared_ptr<Foo> p = factory(arg);
    // 使用 p
}   // p 离开了作用域,它指向的内存会被自动释放掉
  • 由于 p 是 use_factory 的局部变量,在 use_factory 结束时它将被销毁。当 p 被销毁时,将递减其引用计数并检查它是否为 0。在此例中,p 是唯一引用 factory 返回的内存的对象。由于 p 将要销毁,p 指向的这个对象也会被销毁,所占用的内存会被释放

  • 如果有其他 shared_ptr 也指向这块内存,它就不会被释放掉

shared_ptr<Foo> use_factory(T arg){
    shared_ptr<Foo> p = factory(arg);
    // 使用 p
    return p;  // 当我们返回 p 时,引用计数进行了递增操作
}  // p 离开了作用域,但它指向的内存不会被释放掉
  • 注意:如果你将 shared_ptr 存放在一个容器中,而后不再需要全部元素,而只使用其中一部分,要记得用 erase 删除不再需要的那些元素
12.1.3 直接管理内存
  • C++语言定义了两个运算符来分配和释放动态内存。
  • 运算符 new 分配内存,delete 释放 new 分配的内存
  • 相对于智能指针,使用这两个运算符管理内存非常容易出错

使用 new 和 delete 管理动态内存存在的三个常见问题

  • 忘记 delete 内存。忘记释放动态内存会导致人们常说的 "内存泄露" 问题,因为这种内存永远不可能被归还给自由空间了。查找内存泄露错误是非常困难的,因为通常应用程序运行很长时间后,真正耗尽内存时,才能检测到这种错误
  • 使用已经释放掉的对象。通过在释放内存后将指针置为空,有时可以检测出这种错误
  • 同一块内存释放两次。当有两个指针指向相同的动态分配对象时,可能发生这种错误。如果对其中一个指针进行了 delete 操作,对象的内存就被归还给自由空间了。如果我们随后又 delete 第二个指针,自由空间就可能被破坏
  • 注意: 相对于查找和修正这些错误来说,制造出这些错误要简单得多
  • 坚持只使用智能指针,就可以避免所有这些问题。对于一块内存,只有在没有任何智能指针指向它的情况下,智能指针才会自动释放它

空悬指针

delete 一个指针之后,指针值就变为无效了。虽然指针已经无效了,但在很多机器上仍然保存着(已经释放了的)动态内存的地址。在 delete 之后,指针就变成了人们所说的空悬指针,即,指向一块曾经保存数据但现在已经无效的内存的指针

相关文章

  • 第 12 章 动态内存(1)

    全局对象在程序启动时分配,在程序结束时销毁 对于局部自动对象,当我们进入其定义所在的程序块时被创建,在离开时销毁 ...

  • 第12章 动态内存

    静态内存:保存局部static对象,类static数据成员以及定义在任何函数之外的变量。 栈内存:用来保存定义在函...

  • 第12章 动态内存

    生存期全局对象:程序启动时分配,程序结束时销毁局部对象:进入其定义所在的程序块时被创建,离开块时被销毁static...

  • 第12章:动态内存

    #1.动态内存与智能指针1.1 shared_ptr类1.2 直接管理内存1.3 shared_ptr和new结合...

  • 使用标准库:文本查询程序

    C++ Primer 5th 第12章动态内存使用标准库:文本查询程序程序名称:文本查询程序程序功能:允许用户在一...

  • 第 12 章 动态内存(2)

    12.1.3 shared_ptr 和 new 结合使用 定义和改变 shared_ptr 的其他方法 不要使用 ...

  • 【拥抱雪夜】目录

    第1章 第2章 第3章 第4章 第5章 第6章 第7章 第8章 第9章 第10章 第11章 第12章 第13章 第...

  • 9月24日读经(6)

    易经第21-27卦,系辞上传第7-12章;孝经第1-7章,老子第68-75章。

  • 阿里巴巴大数据实践(数据管理篇)

    第12章 元数据第13章 计算管理第14章 存储和成本管理第15章 数据质量第16章 数据应用 第12章 元数据 ...

  • 9月21日读经(4)

    今早读易经第21-27卦,系辞上传第7-12章;孝经第1-7章;老子第68-75章。

网友评论

      本文标题:第 12 章 动态内存(1)

      本文链接:https://www.haomeiwen.com/subject/ftktgqtx.html