【C++】《C++ Primer 》第十二章
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了【C++】《C++ Primer 》第十二章,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含5501字,纯文字阅读大概需要8分钟。
内容图文
![【C++】《C++ Primer 》第十二章](/upload/InfoBanner/zyjiaocheng/1233/f9c5225a22574abc94f7fad01b6c0660.jpg)
第十二章 动态内存
- 目前为止,所使用的对象都有着严格定义的生存期。
- 全局对象在程序启动时分配,在程序结束时销毁。局部自动对象在进入其定义所在的程序块时被创建,在离开块时销毁。局部static对象在第一次使用前分配,在程序结束时销毁。
- 标准库定义了两个智能指针类型来管理动态分配的对象,当一个对象应该被释放时,指向它的智能指针可以确保自动地释放它。
- 静态内存用来保存局部static对象、类static对象以及定义在任何函数之外的变量。栈内存用来保存定义在函数内的非static对象。分配在静态内存或栈内存中的对象由编译器自动创建和销毁。
- 每个程序还拥有一个内存池,即堆。程序用堆来存储动态分配的对象 -- 即那些在程序运行时分配的对象。
一、动态内存与智能指针
- 动态内存管理:
- new:在动态内存中为对象分配空间并返回一个指向该对象的指针。
- delete:接受一个动态对象的指针,销毁该对象,并释放与之关联的内存。
- 智能指针:
- 管理动态对象。
- 行为类似常规指针。重要的区别是它负责自动释放所指向的对象。智能指针也是模板。
- 智能指针的区别:都定义在 头文件memory 中,但管理底层指针的方式不同。
- shared_ptr:允许多个指针指向同一个对象。
- unique_ptr:"独占"所指向的对象。
- weak_ptr的伴随类:它是一种弱引用,指向shared_ptr所管理的对象。
1. shared_ptr类
- shared_ptr 和 unique_ptr 都支持的操作:
操作 | 解释 |
---|---|
shared_ptr unique_ptr |
空智能指针,可以指向类型是T的对象 |
p | 将p用作一个条件判断,若p指向一个对象,则为true |
*p | 解引用p,获得它指向的对象。 |
p->mem | 等价于(*p).mem。 |
p.get() | 返回p中保存的指针,要小心使用,若智能指针释放了对象,返回的指针所指向的对象也就消失了。 |
swap(p, q) p.swap(q) | 交换p和q中的指针。 |
- shared_ptr 独有的操作:
操作 | 解释 |
---|---|
make_shared |
返回一个shared_ptr,指向一个动态分配的类型为T的对象。使用args初始化此对象。 |
shared_ptr |
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共享对象的智能指针数量;可能很慢,主要用于调试。 |
- 最安全的分配和使用动态内存的方法是调用一个名为 mark_shared 的标准库函数。
// 指向一个值为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>();
- shared_ptr的拷贝和赋值:
auto p = nake_shared<int>(42); // p指向的对象只有p一个引用者
auto q(p); // p和q都指向相同对象,此对象有两个引用者
- 可以认为每个shared_ptr都有一个关联的计数器,通常被称为引用计数。当拷贝一个shared_ptr时,计数器就会增加。当shared_ptr赋予一个新值或者shared_ptr被销毁时,计数器就会减少。当计数器变为0时,它就会自动释放自己所管理的对象。
auto r = make_shared<int>(42);
r = q; // r原来指向的对象已经没有引用者,会自动释放
- 到底是用一个计数器还是其他数据结构来记录有多少指针共享对象,完全由标准库的具体实现来定义。
- shared_ptr的析构函数会递减它所指向的对象的引用计数。如果引用计数变为0,shared_ptr的析构函数就会销毁对象,并释放它占用的内存。
- 如果将shared_ptr存放于一个容器中,而后不再需要全部对象,要记得erase那些不再需要的元素。
- 使用动态内存的三种原因:
- 程序不知道自己需要使用多少对象。比如容器类。
- 程序不知道所需要对象的准确类型。
- 程序需要在多个对象间共享数据。
2. 直接管理内存
- 用new动态分配和初始化对象。
- new无法为分配的对象命名(因为自由空间分配的内存是无名的),因此是返回一个指向该对象的指针。
- int *pi = new int(123);
- 一旦内存耗尽,会抛出类型是bad_alloc的异常。
- 用delete将动态内存归还给系统。
- 接受一个指针,指向要释放的对象。
- delete后的指针称为空悬指针(dangling pointer),即指向一块曾经保存数据对象但现在已经无效的内存的指针。
- 使用new和delete管理动态内存存在三个常见问题:
- 1.忘记delete内存。
- 2.使用已经释放掉的对象。
- 3.同一块内存释放两次。
- 由内置指针(而不是智能指针)管理的动态内存在被显式释放前一直都会存在。坚持只使用智能指针可以避免上述所有问题。
3. shared_ptr和new结合使用
- 定义和改变shared_ptr的其他方法:
操作 | 解释 |
---|---|
shared_ptr |
p管理内置指针q所指向的对象;q必须指向new分配的内存,且能够转换为T*类型 |
shared_ptr |
p从unique_ptr u那里接管了对象的所有权;将u置为空 |
shared_ptr |
p接管了内置指针q所指向的对象的所有权。q必须能转换为T*类型。p将使用可调用对象d来代替delete |
shared_ptr |
p是shared_ptr p2的拷贝,唯一的区别是p将可调用对象d来代替delete |
p.reset() | 若p是唯一指向其对象的shared_ptr,reset会释放此对象。若传递了可选的参数内置指针q,会令p指向q,否则会将p置空。若还传递了参数d,则会调用d而不是delete来释放q |
p.reset(q) | 同上 |
p.reset(q, d) | 同上 |
- 不要混合使用普通指针和智能指针。使用一个内置指针来访问一个智能指针所负责的对象是很危险的,因为无法知道对象何时会被销毁。
- get用来将指针的访问权限传递给代码,只有在确定代码不会delete指针的情况下,才能使用get。特别是,永远不要用get初始化另一个智能指针或者为另一个智能指针赋值。
4. 智能指针和异常
- 如果使用智能指针,即使程序块由于异常过早结束,智能指针类也能确保在内存不需要的时候将其释放。
- 智能指针陷阱:
- 不用相同的内置指针初始化(或reset)多个智能指针
- 不delete get()返回的指针。
- 如果你使用get()返回的指针,记得当最后一个对应的智能指针销毁后,你的指针就无效了。
- 如果你使用智能指针管理的资源不是new分配的内存,记住传递给它一个删除器。
5. unique_ptr
- 某一个时刻只能有一个unique_ptr指向一个给定的对象。
- 不支持拷贝或者赋值操作。
- 向后兼容:auto_ptr:老版本,具有unique_ptr的部分特性。特别是,不能在容器中保存auto_ptr,也不能从函数返回auto_ptr。
- unique_ptr操作:
操作 | 解释 |
---|---|
unique_ptr |
空unique_ptr,可以指向类型是T的对象。u1会使用delete来是释放它的指针 |
unique_ptr<T, D> u2 | u2会使用一个类型为D的可调用对象来释放它的指针 |
unique_ptr<T, D> u(d) | 空unique_ptr,指向类型为T的对象,用类型为D的对象d代替delete |
u = nullptr | 释放u指向的对象,将u置为空 |
u.release() | u放弃对指针的控制权,返回指针,并将u置空 |
u.reset() | 释放u指向的对象 |
u.reset(q) | 令u指向q指向的对象 |
u.reset(nullptr) | 将u置空 |
6. weak_ptr
- weak_ptr是一种不控制所指向对象生存期的智能指针。
- 指向一个由shared_ptr管理的对象,不改变shared_ptr的引用计数。
- 一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放,不管有没有weak_ptr指向该对象。
- weak_ptr操作:
操作 | 解释 |
---|---|
weak_ptr |
空weak_ptr可以指向类型为T的对象 |
weak_ptr |
与shared_ptr指向相同对象的weak_ptr。T必须能转换为sp指向的类型 |
w = p | p可以是shared_ptr或一个weak_ptr。赋值后w和p共享对象 |
w.reset() | 将w置为空 |
w.use_count() | 与w共享对象的shared_ptr的数量 |
w.expired() | 若w.use_count()为0,返回true,否则返回false |
w.lock() | 如果expired为true,则返回一个空shared_ptr;否则返回一个指向w的对象的shared_ptr |
二、动态数组
- 大多数应用应该使用标准库容器而不是动态分配的数组,使用容器更加简单、更不容器出现内存管理错误并且有更好的性能。
1. new和数组
- new一个动态数组:
- 类型名之后加一对方括号,指明分配的对象数目(必须是整型,不必是常量)。
- 返回指向第一个对象的指针。
- int *p = new int[size];
- delete一个动态数组:
- delete [] p;
- unique_ptr和数组:
- 指向数组的unique_ptr不支持成员访问运算符(点和箭头)。
操作 | 解释 |
---|---|
unique_ptr<T[]> u | u可以指向一个动态分配的数组,整数元素类型为T |
unique_ptr<T[]> u(p) | u指向内置指针p所指向的动态分配的数组。p必须能转换为类型T* |
u[i] | 返回u拥有的数组中位置i处的对象。u必须指向一个数组 |
2. allocator类
- 标准库allocator类定义在 头文件memory 中,帮助我们将内存分配和对象构造分离开。
- 分配的是原始的、未构造的内存。
- allocator是一个模板。
- allocator
alloc; - 在给定目的位置创建元素,而不是由系统分配内存给他们。
- 标准库allocator类及其算法:
操作 | 解释 |
---|---|
allocator |
定义了一个名为a的allocator对象,它可以为类型为T的对象分配内存 |
a.allocate(n) | 分配一段原始的、未构造的内存,保存n个类型为T的对象 |
a.deallocate(p, n) | 释放从T*指针p中地址开始的内存,这块内存保存了n个类型为T的对象;p必须是一个先前由allocate返回的指针。且n必须是p创建时所要求的大小。在调用deallocate之前,用户必须对每个在这块内存中创建的对象调用destroy |
a.construct(p, args) | p必须是一个类型是T*的指针,指向一块原始内存;args被传递给类型为T的构造函数,用来在p指向的内存中构造一个对象 |
a.destroy(p) | p为T*类型的指针,此算法对p指向的对象执行析构函数 |
- allocator伴随算法:
操作 | 解释 |
---|---|
uninitialized_copy(b, e, b2) | 从迭代器b和e给定的输入范围中拷贝元素到迭代器b2指定的未构造的原始内存中。b2指向的内存必须足够大,能够容纳输入序列中元素的拷贝 |
uninitialized_copy_n(b, n, b2) | 从迭代器b指向的元素开始,拷贝n个元素到b2开始的内存中 |
uninitialized_fill(b, e, t) | 在迭代器b和e执行的原始内存范围中创建对象,对象的值均为t的拷贝 |
uninitialized_fill_n(b, n, t) | 从迭代器b指向的内存地址开始创建n个对象。b必须指向足够大的未构造的原始内存,能够容纳给定数量的对象 |
三、使用标准库:文本查询程序
需求描述:允许用户在一个给定文件中查询单词。查询结果是单词在文件中出现的次数及其所在行的列表。如果一个单词在一行中出现多次,此行只列出一次。行会按照升序输出。
任务拆解:
- 当程序读取输入文件时,它必须记住单词出的现每一行。因此,程序需要逐行读取输入文件,并将每一行分解为独立的单词。
- 当程序生成输出时:
- 它必须能提取每个单词所关联的行号
- 行号必须按升序出现且无重复
- 它必须能打印给定行号中的文本
- code
原文:https://www.cnblogs.com/parzulpan/p/13463320.html
内容总结
以上是互联网集市为您收集整理的【C++】《C++ Primer 》第十二章全部内容,希望文章能够帮你解决【C++】《C++ Primer 》第十二章所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。