C++ Study Notes (12) - Dynamic Memory, Smart Pointers and Dynamic Arrays

C++ Study Notes (12) - Dynamic Memory, Smart Pointers and Dynamic Arrays

Objects used in programs have strictly defined lifetimes.

  • Global object: allocated when the program starts, destroyed when the program ends
  • Local automatic objects: created when the program enters the block in which it is defined, and destroyed when it leaves the block
  • Local staticobjects: allocated before first use, destroyed when program ends
  • Dynamically allocate objects : show creation, show release

memory storage area

  • Static memory: local staticobjects, class staticdata members, and variables defined outside any function
  • staticStack memory: non- objects defined within functions
  • Free space (heap): dynamically allocated objects, the lifetime of dynamically allocated objects is controlled by the program

Dynamic memory and smart pointers

The management of dynamic memory is done through a pair of operators

  • new : Allocate space for an object in dynamic memory and return a pointer to the object, which is used to initialize the object
  • delete : accepts a pointer to a dynamic object, destroys the object, and frees the memory associated with it

There is a problem with the use of dynamic memory in the container

  • Memory leak: forgetting to free memory
  • Illegal memory pointer: The village is released when there are still pointers to refer to the memory

The new standard library provides two types of smart pointers to manage dynamic objects. The difference between smart pointers and regular pointers is that they are responsible for automatically releasing the objects they point to.

  • shared_ptr: allow multiple pointers to the same object
  • unique_ptr: exclusive to the object pointed to
  • weak_ptr: a weak reference that points to shared_ptrthe managed object

shared_ptrkind

shared_ptr_and_unique_ptr_specific_operations

Create a smart pointer

shared_ptr<string> p1;  // shared_ptr, 可以指向 string
shared_ptr<list<int>> p2;  // shared_ptr, 可以指向 int 的 list

make_sharedfunction

make_sharedAllocate a formation in dynamic memory and initialize it, returning a pointer to this object shared_ptr, using its arguments to construct an object of the given type

// 指向一个值为“9999999999” 的 string
shared_ptr<string> p4 = make_shared<string>(10, '9');
// 也可使用 auto, p6 指向一个动态分配的空 vector<string>
auto p6 = make_shared<vector<string>>();

reference counting

  • Increment counter while copying
    • one shared_ptrinitializes the othershared_ptr
    • will shared_ptrbe passed as a parameter to the function
    • will shared_ptrbe the return value of the function
  • Counter decrements
    • When assigning shared_ptra new value to
    • shared_ptrOn destruction (such as when a local shared_ptrobject leaves its scope)
auto r = make_shared<int>(42);  //  r 指向的 int 只有一个引用者
r = q;  // 给 r 赋值,令它指向另一个地址
        // 递增 q 指向的对象的引用计数
        // 递减 r 原来指向的对象的引用计数
        // r 原来指向的对象已没有引用者,会自动释放

shared_ptrDestroy and free memory

shared_ptrThe destructor will decrement the reference count of the object it points to, and if the reference technology changes 0, shared_ptrthe destructor will destroy the object and free the memory it occupies

void function(T arg)
{
    shared_ptr<int> p = make_shared<int>(125);
          // 情况1:不返回, p 为局部变量,函数结束时被销毁,p被销毁时,引用计数递减为0,故内存被释放
    return p;  // 情况2:返回 p 的拷贝, 引用计数进行递增操作,故即使 p 被销毁,但内存还有其他使用者,不会被释放
}

Reasons to use dynamic memory

  • The program does not know how many objects it needs to use, such as container classes
  • The program does not know the exact type of the desired object
  • Programs need to share data among multiple objects

direct memory management

The C++ language defines two operators to allocate and release dynamic memory. Compared with smart pointers, it directly manages container errors.

  • new: Allocate memory and return a pointer to the object.

    // 默认情况下,动态分配对象是默认初始化的,即内置类型或组合类型的对象的值将未定义,类使用默认构造函数
    int *pi = new int;  // pi 指向一个动态分配的、未初始化的无名对象,即默认初始化,`*pi值未定义
    // 也可对动态分配对象进行值初始化,只需在类型名之后跟一对控括号
    int *pi2 = new int();  // 值初始化为0, *pi2 为0
    ///若括号中仅有单一初始化器,可使用 auto 推断想要分配的对象类型
    auto p1 = new auto(obj);  // p1类型是指针,指向从 obj 自动推断出的类型
    auto p2  new auto{a, b, c};  // 错误:括号中只能有单个初始化器
    //用 new 分配 const 对象是合法的, 下为分配并初始化一个 const int
    const int *pci = new const int(1024);
  • delete: Destroy the object pointed to by the given pointer and release the corresponding memory. Must be a pointer to dynamically allocated, or a null pointer. Freeing a block newof memory that was not allocated, or freeing the same pointer multiple times, has undefined behavior

  • Empty dangling town : pointer to a block of memory that once held data objects but is now invalid

shared_ptrand newuse in combination

  • Smart pointer constructor that accepts pointer parameters Yes explicit, so you can't say that a built-in pointer is implicitly converted to a smart pointer, you must use direct initialization

    shared_ptr<int> p1 = new int(1024);  // 错误:必须使用直接初始化方式
    shared_ptr<int> p2(new int(1024));  // 正确:使用了直接初始化方式
    

    shared_ptr_define_and_modify_other_operations

  • Do not mix normal pointers and smart pointers, because it is dangerous for built-in pointers to access the object the smart pointer is responsible for, because there is no way to know when the object will be destroyed

  • Do not use to getinitialize another smart pointer or assign a value to a smart pointer. getDefined in the smart pointer type, usher returns a built-in pointer to the object managed by the smart pointer.

    shared_ptr<int> p(new int(42));  // 引用计数为 1 
    int *q = p.get();  // 正确:但使用 q 时要注意,不要让他管理的指针被释放
    {// 新程序块
        // 未定义:两个独立的`shared_ptr`指向相同的内存
      shared_ptr<int>(q)
    }// 程序块结束,q 被销毁,它指向的内存被释放
    int foot = *p;  // 未定义:p 指向的内存已经被释放了

Smart pointers and exceptions

  • Smart pointers are still properly freed when exceptions occur, but directly managed memory is not freed automatically

    void f()
    {
      shared_ptr<int> sp(new int(42));  // 分配一个新对象
        int *ip = new int(42);  // 动态分配一个新对象
        // 这段代码抛出一个异常,且在 f 中未被捕获
        delete ip;  // 在推出之前释放内存,发生异常时,没有执行,被跳过
    } // 在函数结束时 shared_ptr 自动释放内存
  • Deleter : shared_ptrRelease the pointer saved in , which is used to replace shared_ptrthe default operation when it is destroyed, and can be specified when it is deletecreatedshared_ptr

  • Continuation of this specification for the correct use of smart pointers

    • Do not initialize (or reset) multiple smart pointers with the same built-in pointer value
    • delete``get()pointer that does not return
    • does not use get()initialization or resetanother smart pointer
    • If you use get()the returned pointer, remember that it will be invalidated when the last corresponding smart pointer is destroyed
    • If the resource managed by the smart pointer represents newallocated memory, remember to pass it a deleter

unique_ptr

unique_ptrOwns the object it points to, that is, there can only be one unique_ptrpointer to an object at a time, and when it is destroyed, the pointed object is also destroyed

  • unique_ptrNo similar make_sharedfunction. When defined, it needs to be bound to the newreturned pointer

  • Similarly shared_ptr, initialization unique_ptrneeds to be in the form of direct initialization

  • unique_ptrExclusively the object it points to, so it does not support ordinary copy or assignment operations

    unique_ptr<string> p1(new string("hao"));  // p2 指向一个值为 hao 的 string
    unique_ptr<string> p2(p1);  // 错误:unique_ptr 不支持拷贝
    unique_ptr<string> p3;
    p3 = p2;  // 错误:unique_ptr 不支持赋值
  • unique_ptrAlthough it cannot be assigned or copied, it is possible to call releaseor resettransfer ownership from constone unique_ptrto anotherunique_ptr

    unique_ptr_operations

    // 接上
    unique_ptr<string> p2(p1.release());  // release 将 p1 置为空,并将所有权转移给 p2
  • unique_ptrCan be copied or assigned when it will be destroyed, such as returning from a functionunique_ptr

    unique_ptr<int> clone(int p)
    {
      return unique_ptr<int>(new int(p));
    }
  • Similarly shared_ptr, unique_ptrits default deleter can also be overloaded

    // p 指向一个类型为 objT 的对象,并使用一个类型为 delT 的对象释放 objT对象
    // 它会调用一个名为 fcn 的 delT 类型对象
    unique_ptr<objT, delT> p (new objT, fcn);

weak_ptr

A smart pointer that does not control the lifetime of the pointed-to object, pointing to an shared_ptrobject managed by a. shared_ptrand reference counts that do not change after binding

weak_ptr

auto p = make_shared<int>(42);
weak_ptr<int> wp(p);  // wp 弱共享 p; p 的引用计数未改变
// 由于对象可能不存在,故不能使用 weak_ptr 直接访问队形,而必须调用 lock
if(shared_ptr<int> np = wp.lock())  // 如果 np 不为空则条件成立
{
    // 在 if 中, np 与 p 共享对象
}

dynamic array

The C++ language and the standard library provide two methods for allocating an array of objects one at a time.

  • The C++ language defines another newexpression syntax that allocates and initializes an array of objects
  • The standard library provides allocatorclasses that allow allocation and initialization separation, better performance and more flexibility

newand array

  • newallocating array

    int *pia = new int[5];  // pia 指向第一个 int
    typedef int arrT[42];  // arrT 表示 42 个 int 的数组类型
    int *p = new arrT;  // 分配一个 42 个int 的数组; p 指向第一个 int
  • Although the allocated memory is usually called new T[]a dynamic array, it does not get an array type object, but a pointer to an array element type, that is, a dynamic array is not an array type, and certain functions such as begin, endor range forstatements cannot be used.

  • By default, newallocated objects are default-initialized. You can also perform value initialization, just add it ()after

    int *pia = new int[10];  // 10 个未初始化的 int
    int *pia2 = new int[10]();  // 10 个值初始化为 0 的 int
    int *pia3 = new int[10]{0, 1, 2, 3, 4, ,5};  // 列表初始化,剩余元素执行值初始化
  • An initializer cannot be given in parentheses, i.e. autoan array cannot be allocated with

  • When freeing a dynamic array, add an empty square bracket before the pointer, even if the array is defined with a type alias, the elements are destroyed in reverse order

    typedef int arrT[42];  // arrT 表示 42 个 int 的数组类型
    int *p = new arrT;  // 分配一个 42 个int 的数组; p 指向第一个 int
    delete [] p;  // 方括号必需,因为分配的是一个数组
  • The standard library provides a version newof the array that manages allocationsunique_ptr

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

    unique_ptr_point_to_array

  • If you want to use shared_ptrmanaged dynamic arrays, you must provide a custom deleter

    shared_ptr<int> sp(new int[10], [](int *p){delete[] p;});  // 提供自定义删除器
    sp.reset();  // 使用自定义的 lambda 释放数组,它使用 delete[]

allocatorkind

allocatorA class separates memory allocation from object construction, and its allocated memory is raw, unconstructed.

allocator_class_and_its_algorithms

allocator<string> alloc;  // 可以分配 string 的 allocator 对象
auto const p = alloc.allocate(n);  // 分配 n 个未初始化的 string

auto q = p;  // q 指向最后构造的元素之后的位置
alloc.construct(q++);  // *q 为空字符串
alloc.construct(q++, 10, 'c');  // *q 为 cccccccccc
alloc.construct(q++, "hi");  // *q 为 hi

while(q != p)
    alloc.destroy(--q);  // 释放我们真正构造的 string

alloc.deallocate(p, n);  // 释放内存
  • In order to use allocatorthe returned memory, the constructobject must be constructed with bit-constructed memory, behavior is undefined

  • After use, must be called on each constructed element destroyto destroy, destroyaccept a pointer, and perform a destructor on the pointed object

  • After destruction, this part of the memory can be reused to save others string, or the memory can be released and returned to the system

  • Algorithms for copying and filling uninitialized memory

    allocator_algorithms

    vector<int> vi{1, 2, 3};
    allocator<int> alloc;
    auto p = alloc.allocate(vi.size() * 2);  // 分配比 vi 中元素所占空间大一倍的动态内存
    auto q = alloc.unintialized_copy(vi.begin(), vi.end(), p); //拷贝vi中元素构造从p开始的元素
    uninitialized_fill_n(q, vi.size(), 42);  // 将剩余元素初始化为42

Epilogue

In C++, memory is allocated dynamic memory blocks through the new 表达式分配,通过delete class表达式释放。标准库还定义了一个``allocator

C++ programs should now use smart pointers whenever possible, because direct management is error-prone

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325628734&siteId=291194637