• C++笔记(2) —— 动态内存


    C++笔记(2) —— 动态内存

    简述

    静态内存用来保存局部static对象、类static数据成员、和函数之外的变量,而栈内存用来保存函数内的非static对象。这两部分内存都是经由编译器自动创建和销毁的,而除此之外还有一个内存池,一般被称为堆,是用来存储动态分配的对象。这些对象需要交由程序自己控制生存期。

    直接分配

    malloc和free是C语言的库函数,在栈中动态分配内存,而在C++里面,定义了两个运算符 new/delete 来分配和释放内存。它们可以通过被重载而实现不同于默认分配的新的分配方式,

    • 分配 new:分配一个值,直接使用 new + 类型即可,返回的是该类型的指针。而如果分配数组,则是 new + 类型[数组大小],返回的是指向第一个元素的指针。
    int* pi = new int;                      //直接分配未初始化的对象指针
    const int* p = new const int(1024);     //使用直接初始化方式分配
    int* pArray = new int[10];              //分配数组
    
    • 释放 delete:在动态内存使用过后,需要释放空间,此时需要通过delete来释放。如果释放一个对象,使用 delete + 对象名称。如果释放一个数组,则需要delete[] + 数组名称,一定要记得 [],否则会造成内存泄漏。同时,一个空间被释放后,指针指向的区域就是无效的了,所以在delete之后还需要将原来的指针设为nullptr,否则将会产生空悬指针(dangling pointer)。(当然即使是这样也并不是就完全安全了,如果还有另外的指针指向同一个地址,那么如果不把这个指针设为nullptr,依然会产生问题)
    delete pi;
    delete p;
    delete[] pArray;
    
    pi = nullptr;
    p = nullptr;
    pArray = nullptr;
    

    智能指针

    因为直接分配动态内存十分容易引发问题,所以在c++11标准中为了更容易和更安全的使用动态内存,提供了几种智能指针来方便我们管理动态对象。智能指针与指针的行为很类似,而最重要的区别就在于自动释放对象。智能指针也是一个模板类,在创建的时候同样需要提供指针指向的类型。

    • shared_ptr

      shared_ptr允许多个指针指向同一个对象,使用方式与普通指针类似,可以认为shared_ptr有一个引用计数,通过该计数来自动决定何时销毁。

    shared_ptr<string> p1;
    if (p1 && p1->empty()) *p1 = "hi";  //*操作,->操作,指针判断都可以使用
    shared_ptr<int> p2(new int(33));    //通过直接初始化形式初始化指针
    
    更安全使用shared_ptr来进行动态内存分配的方式是调用make_shared函数。make_shared函数在动态内存中分配一个对象并初始化,返回指向该对象的shared_ptr:
    
    shared_ptr<int> p2 = make_shared<int>(22);
    auto p3 = make_shared<string>("hello");
    
    每当进行一次赋值或者拷贝操作时,shared_ptr都会递增自己的计数器,而每当自身离开某个作用域或者被重新赋值而应被清除时,递减自己的计数器。当销毁计数为最后一个时,shared_ptr就会自动调用析构函数销毁自身,同时释放内存。正是通过这个计数器,shared_ptr实现了智能指针的更安全使用动态内存的方法。
    
    • unique_ptr

      unique_ptr与shared_ptr不同,同一时间只能有一个unique_ptr指向一个给出的对象,当其被销毁时,指向的对象也被销毁。定义unique_ptr时,需要绑定到一个new返回的指针上,所以初始化unique_ptr必须采用直接初始化形式,而且因为只能有一个unique_ptr指向给定的对象,所以不支持拷贝和赋值操作。

    unique_ptr<int> p(new int(10));
    
    虽然不能拷贝和赋值,但可以通过release或reset将所有权转移,release操作返回保存的指针并清空自己:
    
    unique_ptr<string> p2(p1.release());
    unique_ptr<string> p3(new string("test"));
    p2.reset(p3.release());
    
    对于动态数组,也可以使用unique_ptr来管理:
    
    unique_ptr<int[]> up(new int[10]);
    up.release();       //release操作也会自动调用delete[]来销毁指向的数组
    
    • weak_ptr

      weak_ptr指向由shared_ptr指向的对象,但其不控制对象的生命周期,绑定weak_ptr到shared_ptr不会引起引用计数的改变。而如果shared_ptr指向的最后一个对象被销毁引起shared_ptr被销毁,对象就会被释放,而不会理会是否还有weak_ptr指向该对象,所以weak_ptr就是一种弱共享对象。

    shared_ptr<int> p = make_shared<int>(10);
    weak_ptr<int> p2(p);
    
    因为weak_ptr指向的对象可能已经不存在,所以不能直接访问对象,而需要通过调用lock之后,返回的shared_ptr来访问。
    
    if (shared_ptr<int> np = p2.lock())
    {
        //weak_ptr指向的对象存在,并通过np与对象共享
    }
    
  • 相关阅读:
    简易基础版单页面应用
    nginx服务器部署
    vim基础命令
    jsdoc — js注释
    eslint — js书写规范
    stylelint — css书写规范
    gulpfile.js(编译sass,压缩图片,自动刷新浏览器)
    gulp安装使用
    git常用命令
    generator-ivweb 基于react-redux的多页脚手架
  • 原文地址:https://www.cnblogs.com/zhqherm/p/12052010.html
Copyright © 2020-2023  润新知