C++Primer_ch12

第12章 动态内存

12.1 动态内存与智能指针
12.2 动态数组
12.3 使用标准库:文本查询程序


12.1 动态内存与智能指针

  • 对于栈对象,仅在其定义的程序块运行时才存在;static对象在使用之前分配,在程序结束时销毁;除了静态内存和栈内存,每个程序还拥有一个内存池。这部分内存被称为自由空间。程序用堆来存储动态分配对象。动态对象的生存期由程序控制

  • 在C++中,动态内存的管理是通过一对运算符来完成的:
    new,在动态内存中为对象分配空间并返回一个指向该对象的指针,可以选择对对象进行初始化
    delete,接受一个动态对象的指针,销毁该对象,并释放与之关联的内存

  • 新的标准库提供了两种智能指针类型来管理动态对象。新标准库提供的这两种智能指针的区别在于管理底层指针的方式:
    shared_ptr 允许多个指针指向同一个对象
    unique_ptr则独占所指向的对象
    另外还定义了一个名为weak_ptr的伴随类,是一种弱引用,指向shared_ptr所管理的对象。这三种类型都定义在memory头文件中

  • 类似于vector,智能指针也是模版,当创建一个智能指针时,必须提供指针可以指向的类型。默认初始化的智能指针中保存着一个空指针。解引用一个智能指针返回它指向的对象
    在这里插入图片描述 在这里插入图片描述

  • shared_ptr类
    C++Primer_ch12_第1张图片
    C++Primer_ch12_第2张图片
    make_shared函数.
            最安全的分配和使用动态内存的方法就是调用一个名为make_shared函数,此函数在动态内存中分配一个对象并初始化它,返回指向该对象的shared_ptr。make_shared也定义在头文件memory中
    C++Primer_ch12_第3张图片
    传递的参数必须与string的某个构造函数相匹配,通常用auto定义一个对象来保存
    在这里插入图片描述
    shared_ptr的拷贝和赋值.
            当进行拷贝和赋值时,每个shared_ptr都会记录有多少个其他的shared_ptr指向相同的对象。每个shared_ptr都有一个关联的计数器,通常称为其为引用计数拷贝一个shared_ptr ,计数器都会递增,给shared_ptr赋予一个新值或是shared_ptr被销毁,计数器就会递减。一旦一个shared_ptr的计数器变为0,它就会自动释放自己所管理的对象
    C++Primer_ch12_第4张图片
    shared_ptr自动销毁所管理的对象.
            通过另一个特殊成员函数–析构函数完成销毁工作。类似于构造函数,每个类都有一个析构函数。shared_ptr析构函数会递减它所指向的对象的引用计数,如果引用计数变为0,share_ptr的析构函数就会销毁对象,并释放它所占用的内存
    shared_ptr还会自动释放相关联的内存.
            如果将shared_ptr存放于一个容器中,而后不再需要全部元素,而只使用其中一部分,要记得用erase删除不再需要的那些元素

  • 程序使用动态内存出于以下三种原因之一:
            1. 程序不知道自己需要使用多少对象
            2. 程序不知道所需对象的准确类型
            3. 程序需要在多个对象间共享数据
            如果两个对象共享底层的数据,当某个对象被销毁时,不能单方面地销毁底层数据。使用动态内存的一个常见原因是允许多个对象共享相同的状态
    C++Primer_ch12_第5张图片

  • 直接管理内存.运算符new分配内存,delete释放new分配的内存。直接管理内存的类与使用智能指针的类不同,它们不依赖类对象拷贝、赋值和销毁操作的任何默认定义

  • 使用new动态分配和初始化对象.在自由空间分配的内是无名的 ,因此new无法为其分配的对象命名,而是返回一个指向该对象的指针。此new表达式在自由空间构造一个int型对象,并返回指向该对象的指针。默认情况下,动态分配的对象是默认初始化的,未定义,而类类型对象将用默认构造函数进行初始化。可以使用直接初始化方式、传统的构造方式、列表初始化方式值初始化方式
    在这里插入图片描述
    可以使用auto从初始化器来推断我们想要分配的对象的类型。只有括号中仅有单一初始化器时才可以使用auto
    在这里插入图片描述

  • 用new分配const对象时合法的,一个动态分配的const对象必须进行初始化
    在这里插入图片描述

  • 当程序用光了所有可用的内存,new表达式就会失败,抛出一个类型为bad_alloc的异常。可以改变使用new的方式来阻止抛出异常。
    在这里插入图片描述
    这种形式的new为定位new,定位new表达式允许我们向new传递额外的参数。如果将nothrow传递给new,意图时告诉它不能抛出异常

  • 释放动态内存.死了防止内存耗尽,在动态内存使用完毕后,通过delete表达式来将动态内存归还给系统。delete表达式接受一个指针,指向我们想要释放的对象:delete p , p必须指向一个动态分配的对象或是一个空指针。delete表达式也执行两个动作,销毁给定的指针指向的对象;释放对应的内存。释放一块并非new分配的内存,或者是将相同的指针值释放多次,其行为是未定义的。
    C++Primer_ch12_第6张图片

  • 动态对象的生存期直到被释放时为止.由内置指针(而不是智能指针)管理的动态内存在被显式释放前一直都会存在
    C++Primer_ch12_第7张图片
    C++Primer_ch12_第8张图片
    C++Primer_ch12_第9张图片

  • 使用new和delete管理动态内存存在三个常见问题. 1. 忘记delete内存。2. 使用已经释放掉的对象。3. 同一块内存释放两次

  • 在delete之后,指针就变成了空悬指针。即指向一块曾经保存数据对象但现在已经无效内存指针。避免空悬指针的问题:在指针即将要离开器作用域之前释放掉它关联的内存,这样在指针关联的内存被释放掉之后,就没有机会继续使用指针了。如果需要保留指针,可在delete之后将nullptr赋予指针。
    在这里插入图片描述

  • shared_ptr 和new结合使用.可以用new返回的指针来初始化智能指针
    在这里插入图片描述
    接受指针参数的智能指针构造函数是explicit的。因此不能将一个内置指针隐式转换为一个智能指针,必须使用直接初始化形式
    在这里插入图片描述
    类似的
    C++Primer_ch12_第10张图片

C++Primer_ch12_第11张图片
C++Primer_ch12_第12张图片

  • 不要混合使用普通指针和智能指针.
    C++Primer_ch12_第13张图片
    在这里插入图片描述
    在这里插入图片描述

  • 也不要使用get初始化另一个智能指针或为智能指针赋值.智能指针类型定义了一个名为get的函数,它返回一个内置的指针,指向智能指针管理的对象。只有在确定代码不会delete指针的情况下,才能使用get
    C++Primer_ch12_第14张图片

  • 可以用reset来将一个新的指针赋予一个shared_ptr
    在这里插入图片描述
    在这里插入图片描述

  • 智能指针和异常.
    C++Primer_ch12_第15张图片
    函数退出两种情况,正常结束或者异常结束,无论哪种情况,局部对象都会被销毁,上面sp在销毁时会检查引用计数,是指向内存唯一指针,因此内存会被释放掉。与之相对,直接管理的 内存是不会自动释放的,在new之后,delete之前发生异常,则内存不会被释放。
    C++Primer_ch12_第16张图片
    异常未在f中被捕获,则内存永远不会被释放了。在函数f之外没有指针指向这块内存,因此就无法释放了。

  • 智能指针和哑类.如果connection有一个析构函数,就可以在f结束时由析构函数自动关闭连接。
    C++Primer_ch12_第17张图片

  • 使用我们自己的释放操作.当p被销毁时,它不会对自己保存的指针执行delete,而是调用end_connection
    在这里插入图片描述C++Primer_ch12_第18张图片

  • unique_ptr.与shared_ptr不同,某个时刻只能有一个unique_ptr指向一个给定对象。当unique_ptr被销毁时,它所指向的对象也被销毁。当定义一个unique_ptr时,需要将其绑定到一个new返回到指针上
    在这里插入图片描述
    由于一个unique_ptr拥有它指向的对象,因此unique_ptr支持普通的拷贝或赋值操作
    C++Primer_ch12_第19张图片
    虽然不能拷贝或赋值unique_ptr,但可以通过release或reset将指针的所有权从一个(非const)unique_ptr转移给另一个unique:
    C++Primer_ch12_第20张图片
    release成员返回unique_ptr当前保存的指针并将其置为空。调用release会切断unique_ptr和它原来管理的对象间的联系。不能拷贝unique_ptr的规则有一个例外:可以拷贝或赋值一个将要被销毁的unique_ptr,最常见的例子是从函数返回一个unique_ptr
    C++Primer_ch12_第21张图片
    向unique_ptr传递删除器.unique_ptr默认情况下用delete释放它所指向的对象。与shared_ptr一样,可以重载一个unique_ptr中默认的删除器
    在这里插入图片描述
    C++Primer_ch12_第22张图片

  • weak_ptr. weak_ptr是一种不控制所指向对象生存期的智能指针,它指向由一个shared_ptr管理的对象。将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。即使有weak_ptr指向对象,对象也还是会被释放
    C++Primer_ch12_第23张图片
    在这里插入图片描述
    由于对象可能不存在,不能使用weak_ptr直接访问对象,而必须调用lock。此函数检测weak_ptr指向的对象是否仍存在,如果存在,lock返回一个指向共享对象的shared_ptr
    在这里插入图片描述


12.2 动态数组

  • C++语言定义了另一种new表达式语法,可以分配并初始化一个对象数组。标准库中哦包含一个名为allocator的类,允许我们将分配和初始化分离。大多数应用应该使用标准库容器而不是动态分配的数组。使用容器更为简单、更不容易出现内存管理错误并且可能有更好的性能

  • new和数组
    在这里插入图片描述
    方括号中的大小必须是整型,但不必是常量
    在这里插入图片描述
    当用new分配一个数组时,得到的不是数组类型的对象,而是一个数据元素类型的指针.由于分配的内存并不是一个数组类型,因此不能对动态数组调用begin和end,也不能用范围for语句来处理动态数组中的元素
    C++Primer_ch12_第24张图片
    虽然用空括号对数组中的元素进行值初始化,但不能在括号中给出初始化器,这意味着不能用auto分配数组。动态分配一个空数组时合法的
    C++Primer_ch12_第25张图片
    n=0,调用new[n]合法。对于零长度的数组来说,此指针就像尾后指针一样
    在这里插入图片描述

  • 释放动态数组特殊形式delete,在指针前加上一个空括号对。数组中的元素逆序销毁,即最后一个元素首先被销毁,以此类推
    在这里插入图片描述

  • 智能指针和动态数组.标注库提供一个可以管理new分配的数组的unique_ptr版本,为了使用unique_ptr 管理动态数组,需要在对象类型后面跟一对空括号
    在这里插入图片描述C++Primer_ch12_第26张图片
    与unique_ptr不同,shared_ptr不直接支持管理动态数组。如果希望使用shared_ptr管理一个动态数组,必须提供资金定义的删除器
    我使用
    本例中,传递给shared_ptr一个lambda,作为删除器,使用delete[]释放数组。如果未提供删除器,这段代码将是未定义的。默认情况下,shared_ptr使用delete销毁所指向的对象。如果此对象是一个动态数组,对其使用delete所产生的问题与释放一个动态数组指针时所忘记[]产生的问题一样
    在这里插入图片描述
    shared_ptr未定义下标运算符,而且智能指针类型不支持指针算术运算。因此,为了访问数组中的元素,必须使用get获取一个内置指针,然后用它来访问数组元素

  • allocator类.new将内存分配和对象构造组合在一起,delete将对象析构和内存释放组合在一起。一般情况下,将内存分配和对象构造组合在一起可能会导致不必要的浪费。标准库allocator类定义在头文件memory中,它帮助我们将内存分配和对象构造分离开来。allocator是一个模块,为了定义一个allocator对象,必须指明这个allocator可以分配的对象类型。当一个allocator对象分配内存时,它会根据给定的对象类型来确定恰当的内存大小和对齐位置
    在这里插入图片描述
    C++Primer_ch12_第27张图片
    allocator分配的内存是未构造的,需要用construct构造对象。使用未构造的内存,其行为是未定义的。construct成员函数接受一个指针和零个或多个额外参数,在给定位置构造一个元素。额外参数用来初始化构造的对象。
    在这里插入图片描述
    当用完对象后,必须对每个构造的元素调用destroy来销毁它们。我们只能对真正构造了的元素进行destroy操作
    在这里插入图片描述
    拷贝和填充未初始化内存的算法
    C++Primer_ch12_第28张图片


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

你可能感兴趣的:(编程语言,#,C++Primer)