• C++ | 动态内存


    内容

    1. 动态内存和智能指针
      1. shared_ptr
      2. 直接管理内存
      3. 和new一起使用shared_ptr
      4. 智能指针和异常
      5. unique_ptr
    2. 动态数组
    3. 使用库:文本查询程序
    4. 总结

    0. 摘要

    动态分配的对象(dynamically allocated objects)的生命(lifetime)独立于其被创建的地方,他们一直会存在到被明确释放(free)。

    为了让使用动态分配的对象更安全,库定义了两个智能指针(smart pointer)类型来管理动态分配的对象。智能指针能确保在合适的时候自动释放其指向的对象。

    • static memory and stack memory
      • static memory:
        • for local static objects
        • for class static data members
        • for variables defined outside any function
      • stack memory
        • for nonstatic objects defined inside functions
      • automatically created and destroyed by the compiler; static objects are allocated before they are used and are destroyed when the program ends.
    • heap memory (free store)
      • for dynamically allocated objects, that is, objects that the program allocate at run time.
      • must explicitly destroy

    1. 动态内存和智能指针

    operators:

    • new: 分配内存,初始化对象,返回一个指向那个对象的指针;
    • delete: 销毁对象,释放内存。

    动态内存难点:

    • 内存泄露(memory leak):忘记释放内存
    • 非法指针:指向被释放的内存

    智能指针(C++ 11):像普通指针一样使用,并且拥有一个重要特性,可以自动删除其指向的对象。

    • shared_ptr: allows multiple pointers to refer to the same object
      • weak_ptr: a weak reference to an object managed by a shared_ptr
    • unique_ptr: owns the object to which it points

    1.1 shared_ptr class

    像vector一样,智能指针也是模板。因此,在创建一个智能指针时,需要提供另外的信息,也就是其指向对象的类型。

    shared_prt<string> p1; // shared_ptr可以指向一个字符串

    使用智能指针的方式与使用一个指针类似。去引用(dereference)一个智能指针会返回其指向的对象。

    // 如果p1不是空指针,检查其指向的空字符串 if (p1 && p1->empty()) *p1 = "hi"; // 去引用并赋新值给字符串

    make_shared函数

    分配和使用动态内存最安全的方式是调用一个库函数,“make_shared”。

    #include <memory> // for the use of 'make_shared'
    
    // shared_ptr that points to an int with value 42
    shared_ptr<int> p3 = make_shared<int>(42);
    // p4 points to a string with value 999999999
    shared_ptr<string> p4 = make_shared<string>(10, '9');
    // p5 points to an int that is value initialized to 0
    shared_ptr<int> p5 = make_shared<int>();
    

    也可以使用auto来简化,

    // p6 points to a dynamically allocated, empty vector<string> auto p6 = make_shared<vector<string>>();

    复制和赋值给shared_ptrs

    当复制或者赋值给一个 shared_ptr ,各个 shared_ptr 追踪多少个其他 shared_ptrs 指向同一个对象。

    我们可以想象一个 shared_ptr 好像有一个对应的计数器,通常指代为一个 reference count 。每当我们复制一个 shared_ptr ,计数会递增一位。一旦一个 shared_ptr 的计数器归零,这个 shared_ptr 会自动释放他管理的对象。

    shared_ptrs会自动销毁他们的对象

    当最后一个指向某对象的 shared_ptr 被销毁,那 shared_ptr 类会自动销毁其指向的对象。这个销毁动作通过另一个特殊成员函数 destructor 来完成。

    ...并自动释放对应的内存

    注意:如果你把 shared_ptrs 放进一个容器,并且之后需要使用其中一些但不是全部元素,记得把那些不需要的去除(erase)。

    拥有动态生命周期资源的类

    出于下面的目的,程序需要使用动态内存,

    1. 不知道需要多少对象;
    2. 不知道对象需要怎样的类型;
    3. 想要在许多对象间共享数据。

    注意使用动态内存的一个常见原因是允许多个对象共享数据。

    StrBlob类

    暂略

    1.2 直接管理内存

    C++本身定义了两个操作符来分配和释放动态内存。“new”操作符分配内存,“delete”释放由“new”分配的内存。

    使用这两个操作符去管理内存被认为是比使用智能指针更容易出错的。而且,类直接管理自己内存的话,不像那些使用智能指针的类,这些类对于成员复制、赋值和销毁类对象,不可以依赖默认的定义。结果就是,在程序中使用智能指针使编写和调试更加容易。

    1.3 和 new 一起使用 shared_ptr

    智能指针的构造函数(constructors)显式地(explicitly)接受指针。因此,我们不可以隐式地把一个内置指针转换为一个智能指针,我们必须使用初始化的直接形式来初始化智能指针:

    shared_ptr<int> p1 = new int(1024); // error: must use direct initialization

    shared_ptr<int> p2(new int(1024)); // ok: uses direct initialization

    同样的,函数返回值也不可以隐式地转换为智能指针。我们必须显式地绑定一个 shared_ptr 给一个我们想返回的指针:

    shared_ptr<int> clone(int p) { // ok: explicitly create a shared_ptr<int> from int* return shared_ptr<int>(new int(p)); }

    不要混淆普通指针和智能指针

    并且不用使用 get 来初始化或者赋值给另一个智能指针

    其他 shared_ptr 操作

    1.4 智能指针和异常(Exceptions)

    1.5 unique_ptr

    和shared_ptr一样,必须使用直接初始化的形式来初始化unique_ptr:

    unique_ptr<double> p1; // unique_ptr that can point at a double
    unique_ptr<int> p2(new int(42)); // p2 points to int wihch with value 42
    

    因为unique_ptr拥有它指向的对象,unique_ptr不支持普通的复制和赋值操作:

    unique_ptr<string> p1(new string("Stegosaurus"));
    unique_ptr<string> p2(p1); // error: no copy for unique_ptr
    unique_ptr<string> p3;
    p3 = p2; // error: no assign for unique_ptr
    

    我们不能复制或者赋值,但是可以转移所有权到另一个unique_ptr,通过调用release和reset:

    // transfer ownership from p1 (which points to the string Stegosaurus) to p2
    unique_ptr<string> p2(p1.release()); // release make p1 null
    
    // transfer ownership from p3 to p2
    unique_ptr<string> p3(new string("Tres"));
    p2.reset(p3.release()); // reset deletes the memory to which p2 had pointed
    

    调用release打破了unique_ptr和其管理的对象之间的联系。通常,被release返回的指针会被用来初始化另一个智能指针。这样,管理那段内存的责任就从一个智能指针转移到了另一个。

    但是,如果我们不用另一个智能指针来接收从release返回来的指针,我们的程序就要负责释放那片资源。

    只有一个例外情况,我们可以复制和赋值给一个将要被销毁的unique_ptr。

    向后兼容: auto_ptr
    在之前的版本中,库包含了一个叫auto_ptr的类,它有着部分但并非全部unique_ptr的属性。尤其是,auto_ptr不能被保存在一个容器中,也不能作为函数的返回类型。
    虽然auto_ptr还是标准库的一部分,现在的程序应该使用unique_ptr。


    2. 动态数组

    3. 使用库:文本查询程序

    4. 总结

    示例程序

    https://www.cnblogs.com/tenosdoit/p/3456704.html

    参考

    • 《C++ Primer, Fifth Edition》, Stanley B. Lippman
  • 相关阅读:
    2.16.8.内核启动的C语言阶段5
    2.16.7.内核启动的C语言阶段4
    2.16.6.内核启动的C语言阶段3
    2.16.5.内核启动的C语言阶段2
    JAVA_SE基础——34.static修饰成员变量
    JAVA_SE基础——33.this关键字的练习
    JAVA_SE基础——32.this关键字调用本类的构造方法
    JAVA_SE基础——31.this关键字
    JAVA类的方法调用和变量(全套)
    JAVA_SE基础——30.构造代码块
  • 原文地址:https://www.cnblogs.com/casperwin/p/12563628.html
Copyright © 2020-2023  润新知