C++ Primer:Sec 12:动态内存

Sec12 动态内存

12.1 动态内存与智能指针

  • new:在动态内存中为对象分配空间,并返回一个指向该对象的指针
    delete:接受一个指针,销毁对象,释放空间

  • 智能指针:
    smart pointer
    它负责自动释放所指向的对象

    • shared_ptr
      也是模板。要指定指向类型

      make_shared<T>(args);
      shared_ptr<T>p(q);	// 会递增q中的计数器,q中的指针必须能转换为T*
      p = q;				// 递减p的引用计数,递增q的引用计数,若计数为0,则释放
      p.unique();
      p.use_count();
      
    • 使用例子:

      auto p6 = make_shared<vector<string>>();
      
    • 使用动态内存的原因

      • 程序不知道自己需要使用多少对象
      • 程序不知道所需对象的准确类型
      • 程序需要在多个对象间共享数据
  • new和delete

    • placement new:定位new
      int *p2 = new (nothrow) int;

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

      • 一定要记得释放:
        反例:

         Foo* factory(T arg){
             return new Foo(arg);
         }
        
        void use_factory(T arg) {
            Foo *p = factory(arg);
            // 使用了p但是没有delete
        }	// p离开作用域,但p指向的内存没有释放!
        
      • 建议:
        坚持只使用智能指针,可以避免所有这些问题

      • delete一个指针后,指针值就变为无效了,虽然指针已经无效,但是再很多机器上指针仍然保存着已经释放了的动态内存的地址,delete之后指针变成孔璇指针,dangling pointer,即,指向一块曾经保存数据对象但现在已经无效的内存的指针

  • shared_ptr和new结合使用
    可以用new返回的指针来初始化智能指针

    shared_ptr<T> p(q);
    shared_ptr<T> p(u);	// uniuque_ptr u
    shared_ptr<T> p(q, d);	// p接管了内置指针q指向的对象的所有权,而且p将使用可调用对象d,来代替delete (lambda)
    p.reset();
    p.reset(q);
    p.reset(q, d);
    
    • 不要混合使用普通指针和智能指针
  • 可以用reset来将一个新的指针赋予一个shared_ptr

    if(!p.unique())
        p.reset(new string(*p));	// 不是唯一用户,分配新的拷贝
    *p += newVal;	// 唯一用户,可以改变对象的值
    
  • 智能指针与异常
    即使程序块过早结束,比如异常,也可以提早结束

  • 使用我们自己的释放操作
    删除器 deleter

    • 分配了资源,又没有定义析构函数来释放这些资源的类,可能会发生内存泄漏
  • unique_ptr
    一个unique_ptr拥有它所指向的对象
    某个时刻,只能有一个unique_ptr指向一个给定对象
    定义的时候需要绑定给到一个new返回的指针上。且不支持普通的拷贝或赋值操作

    unique_ptr<string> p1<new string(":wdawdawdaw")>;
    unique_ptr<string> p2(p1);	// 错误
    unique_ptr<string> p3;	p3 = p2;	// 错误
    
    • release:
      u放弃对指针的控制权,返回指针,并将u置为空

      返回的指针通常用来初始化或者赋值另一个智能指针

    • 注:我们可以拷贝或者赋值一个将要被销毁的unique_ptr。最常见的例子是从函数返回一个unique_ptr

  • weak_ptr
    是一种不控制所指向对象生存期的智能指针。指向一个由shared_ptr管理的对象,将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数

12.2 动态数组

  • new和数组

    // 1
    int *pia = new int[get_size()];
    
    // 2
    typedef int arrT[42];
    int *p = new arrT;
    
    • 初始化

      int *pia = new int[10];	// 未初始化
      int *pia = new int[10]();	// 初始化为0
      // 也可以列表初始化
      
    • 释放动态数组

      delete p;	// p 指向一个动态分配的对象或者空
      delete [] pa;	// pa指向一个动态数组或者空	
      // 第二个语句销毁pa指向数组的元素,并释放对应的内存。按逆序销毁
      
  • 智能指针和动态数组
    标准库提供了一个可以管理new分配的数组的unique_ptr版本

    unique_ptr<int[]> up(new int[10]);	// up指向一个包含10个未初始化int 的数组
    up.release();	// 自动用delete[] 销毁其指针
    

    注意:shared_ptr不直接支持管理动态数组/。必须提供顶一个删除器

  • allocator类
    定义在头文件memory中,帮助我们将内存分配和对象构造分离开。

    allocator<string> alloc;			// 可以分配string的allocator对象
    auto const p = alloc.allocate(n);	// 分配n个未初始化string
    
    • allocator分配未构造的内存
      我们按需要再此内存中构造对象。
      construct成员函数接受一个指针和0个或者多个额外参数
    • 用完对象后,必须对每个构造的元素调用destroy来销毁。destory接受一个指针,对指向的对象执行析构函数
    • 释放内存通过调用deallocate来完成。
  • 拷贝和填充未初始化内存的算法

    uninitialized_copy(b, e, b2);
    uninitialized_copy_n(b, n, b2);
    
    uninitialized_fill(b, e, t);
    uninitialized_fill_n(b, n, t);
    
  • 使用标准库实例:文本查询程序
    TextQuery

posted @ 2022-12-21 11:17  M1kanN  阅读(35)  评论(0编辑  收藏  举报