C++学习笔记(十一)

第12章 动态内存

1. 动态内存的对象的生存期与它们在那里创建是无关的,只有当显示地被释放时,这些对象才被销毁。

2. 程序用堆(或自由空间)来存储动态分配的对象,即那些在程序运行时分配的对象。

3. 动态内存的管理是通过一对运算符来完成的:

    new 在动态内存中为对象分配空间并返回一个指向该对象的指针;

    delete 接受一个动态对象的指针,销毁该对象,并释放与之关联的内存。

3. 动态内存的使用是很容易出现问题的,因为确保在正确的时间释放内存是极其困难的。有时我们忘记释放内存,在这种情况下就会产生内存泄漏;有时在尚有指针引用内存的情况下我们就释放了它,在这种情况下就会产生引用非法内存的指针。

4. 为了更容易的使用动态内存,新的标准库提供了两种智能指针类型来管理动态对象。智能指针的行为类似常规指针,重要的区别是它负责自动释放所指向的对象。

5. 新标准库提供的两种智能指针的区别在于管理底层指针的方式:

    shared_ptr 允许多个指针指向同一个对象

    unique_ptr 独占所指向的对象

除此之外还定义了一个weak_ptr的伴随类,它是一种弱引用,指向shared_ptr所管理的对象。

这三种类型都定义在头文件memory中。

6. 智能指针也是模板,当创建一个智能指针时,必须提供额外的信息——指针可以指向的类型。

    shared_ptr<string> p1;

    默认初始化的智能指针中保存着一个空指针。

7. 最安全的分配和使用动态内存的方法是调用一个名为make_shared的标准库函数,返回指向此对象的shared_ptr。

    shared_ptr<string> p2 = make_shared<string>( );

    或 auto p2 = make_shared<string>( );

    调用make_shared<string> 时传递的参数必须与string的某个构造函数相匹配。如果不传递参数,对象将会进行值初始化。

8. 每个shared_ptr都有一个关联的计数器,成为引用次数。无论何时拷贝一个shared_ptr,计数器都会增加。

    auto r = make_shared<int>(42);              //r 引用为1次

     r=q;                                                                //r 引用次数为0 ,自动释放

     q 的计数器会+1,r 的计数器会-1

9. 一旦一个shared_ptr的计数器变为0,它就会自动释放自己所管理的对象。

    当指向一个对象的最后一个shared_ptr被销毁时,shared_ptr会通过其成员函数—析构函数来完成销毁工作,析构函数控制此类型的对象销毁时做什么操作。

    shared_ptr的析构函数会递减它所指向的对象的引用计数,如果引用计数变为0,shared_ptr的析构函数就会销毁对象,并释放它占用的内存。

10. 使用动态内存的三个原因:

(1)程序不知道自己需要使用多少对象(2)程序不知道所需对象的精确类型(3)程序需要在多个对象间共享数据

11. C++定义了两个运算符来分配和释放动态内存,new和delete。相对于智能指针,使用这两个运算符管理内存非常容易出错,所以不建议使用。

12. 在自由空间分配的内存是无名的,因此new无法为其分配的对象命名,而是返回一个指向该对象的指针:

       int *pi =new int ;  pi指向一个动态分配的,未初始化的无名对象。

此new表达式(step 1)在自由空间中创造一个int型对象,(step 2)并返回指向该对象的指针。

也可以对其进行值初始化,只需要在类型名后添加一对括号:   string *pi= new string( );     当然也可使用auto:  auto pi=new string( );

13. 虽然计算机通常都配备大容量的内存,但是自由空间被耗尽的情况还是有可能发生的。为了防止内存耗尽,在动态内存使用完毕后,必须将其归还给系统,通过delete表达式来进行。

       delete pi;   //pi必须指向一个动态分配的对象或是一个空指针

delete与new类似,同样执行两步操作:step1 销毁给定的指针指向的对象;step 2 释放对应的内存。

释放一块非new分配的内存,或者将相同的指针值释放多次,其行为是未定义的。

14. 通常情况下,编译器不能分辨一个指针指向的是静态还是动态分配的对象。类似的,编译器也不能分辨一个指针所指向的内存是否已经被释放。对于一个内置指针管理的动态对象,直到被显示释放之前都是存在的。因此,返回指向动态内存(而不是智能指针)的函数,调用者必须记得释放内存。忘记释放内存会导致内存泄漏。

15. 当delete一个指针之后,指针值就无效了,但指针任然保存着已经释放了的动态内存的地址。在delete之后,指针就变成了空悬指针,即,指向一块曾经保存数据对象但现在已经无效的内存的指针。避免空悬指针的方法:在指针即将离开其作用域之前释放掉它所关联的内存。

空悬指针、野指针、内存泄漏、内存溢出

悬垂指针(Dangling pointer)和野指针(Wild pointer)

16. weak_ptr 是一种不控制所指向对象生存期的只能指针,它指向由一个shared_ptr管理的对象。将一个weak_ptr 绑定到一个shared_ptr不会改变shared_ptr的引用计数。一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放,即使有weak_ptr指向对象,对象也还是会被释放。

17. new和delete一次分配释放一个对象,但如果想要一次性为很多对象分配内存,就需要使用动态数组。可以使用new,也可以使用标准库中包含一个allocator类,其允许我们将分配和初始化分离。

18. 为了让new分配一个对象数组,要在类型名后加一对方括号,指明要分配的对象的数目:

     int *pa = new int[42];           string *pi = new string[get_size()];

19. 虽然称new分配的内存成为“动态数组”,但这种叫法某种程度有些误导。当用new分配一个数组时,并未得到一个数组类型的对象,而是得到一个数组元素类型的指针。所以分配的内存并不是一个数组类型,因此不能调用begin和end。

20. 初始化动态内存分配对象的数组:

      int *pi = new int[10]();                                     //10个值初始化为0的int;

      int *pp= new int[10]{1,2,3,4,5,6,7,8,9,0};    //用初始化器的花括号列表来初始化

21. 虽然不能创建一个大小为0的静态数组对象,但是当n=0时,调用new[n]是合法的。new会返回一个合法的非空指针,此指针就像尾后指针一样。但此指针不讷讷个解引用,因为不指向任何元素。

22. 释放动态数组:

      delete p;             //p必须指向一个动态分配的对象或为空

      delete [ ]p;          //p必须指向一个动态分配的数组或为空,数组中的元素按逆序销毁

23. new        将内存分配和对象构造组合在了一起

      delete     将对象析构和内存释放组合在了一起

      标准库allocator类帮助我们将内存分配和对象构造分离开。allocator也是一个模板。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值