C++学习笔记----6、内存管理(一)---- 使用动态内存(1)

        当你使用现代结构,例如std::vector,std::string等等,从一开始到现在以及到未来,C++是一个安全的编程语言。该语言提供了许多的道路,路线以及红绿灯,比如C++核心指导,静态代码分析器来分析代码的正确性,等等。

        然而,C++依然允许你出轨。一个出轨的例子就是手动管理内存(分配与释放内存)。对于C++编程这种手动管理内存是一个特别容易出错的领域。为了写出高质量的C++程序,专业的C++程序员需要理解内存在底层是怎么工作的。前面的博文也介绍过一点儿,我们会继续讨论动态内存的陷阱以及避免以及消除它们的一些技巧。

        因为专业的C++程序员会碰到底层内存处理的代码,所以我们会讨论这一部分的内容。然而,在现代C++代码中,你应该尽量避免底层内存操作。例如,不要使用C风格的数组进行动态内存分配,要使用像vector这样的标准库容器,它们会为你自动处理所有的内存管理。不要用原始的指针,要用智能指针,比如unique_ptr和shared_ptr,后面我们慢慢讨论,它们会自动释放分配的资源,像不再需要的内存等。本质上来说,就是不要再去调用像new/new[]和delete/delete[]这样的内存分配函数了。当然,这不总是可以的,在原有的代码中,可能不是这样,所以做为一个专业的C++程序员,你依然需要知道内存在底层是如何工作的。你可以不使用这些底层的操作,但不代表你可以不明白。

        在现代C++代码中,应该尽可能地避免进行内存底层操作,当牵涉到属主的时候避免原始指针,避免使用旧的C风格的结构与函数。反过来,要使用安全的C++替代方法,例如自动管理内存的对象,像C++的string类,vector容器,智能指针等等。

        好了,言归正传吧,我们先来讨论一下动态内存的使用。

        内存是一个底层的部件 ,对于计算机来说,有时候很不幸地成为像C++这样的高级编程语言来说也是一个不祥之物。当然了,真正理解了动态内存是如何工作的对于成长为一个专业的C++程序员非常重要。

1、如何展示内存

        如何 你有一个内存对象的在大脑当中的模型的话,那理解起动态内存来就会容易得多了。我们的表示方法就是在一个方框旁边一个标记。这个标记就是一个对这个内存对应的名字。方框内的数据就是这个内存的当前值。

        举个例子,下图显示了以下代码执行为的内存状态。这段代码应该在一个函数中,所以变量是一个局部变量。

int i { 7 };

        i就是一个在栈上分配了空间的自动变量。当程序流离开它声明的范围时会自动释放。

        当你使用new关键字时,内存就会在自由空间内存上进行分配。如果没有显式初始化,通过对new的调用分配的内存就没有初始化;也就是说,内存空间的值是任何可能的随机值。我们会以一个?来代表这种没有初始化的状态。以下代码生成了一个ptr的变量,在栈上用nullptr进行了初始化,然后在自由空间内存上给ptr指针分配了内存:

int* ptr { nullptr };
ptr = new int;

          也可以用一行代码来实现:  

int* ptr { new int };

        下图展示了代码执行后的内存状态。

        注意变量ptr仍然在栈上,即使它指向了自由内存空间。指针只是一个变量,可以在栈上或自由内存空间存在。虽然这个事实很容易被遗忘。然而,动态内存总是在自由内存空间进行分配。

        提醒一下,C++核心指导指出,每一次声明指针变量的时候,都要立即用合适的指针或者nullptr进行初始化。不要留后患。

        下面的例子展示了指针可以在栈上也可以在自由内存空间存在。

int** handle { nullptr };
handle = new int*;
*handle = new int;

        这段代码首先声明了一个指向指向整数的指针变量handle。它就动态地分配的足够的内存来保存一个指向整数的指针,在handle中保存指向新内存的指针。然后,内存(*handle)被分配给了一个指向另一块足够保存整数的动态内存片的指针。下图展示 了两层指针,一个指针位于栈上(handle),另一个位于自由内存空间(*handle)。

2、分配与释放内存

        为变量分配空间,需要作用use关键字。释放该空间可以让程序的其他部分使用,需要使用delete关键字。

2.1、使用use和delete

        当你想要分配一块儿内存,你需要调用带有该类型变量需要的空间的new关键字。new返回一个指向该内存的指针,保存这个指针变量就靠你了。如果你忽略掉new的返回值或者指针变量越界,内存就会变成孤儿,因为无法再访问到它。这就叫做内存泄露。

        例如,下面的代码就使用保存int的内存变成了孤儿。下图展示了代码执行后的内存状态。

void leaky()
{
    new int; // BUG! 内存泄露!
    println("I just leaked an int!");
}

        当有大块的数据在自由内存空间无法访问,从栈上不管是直接访问或者间接访问都无法做到,那么这块内存就是孤儿或者内存泄露了。

        在找到让计算机无限地供给内存之前,需要告诉编译器当与之相连的对象可以被释放用于其他目的。为了释放自由内存空间上的内存,需要使用delete关键字后跟指向内存的指针,如下代码所示:

int* ptr { new int };
delete ptr;
ptr = nullptr;

        从经验上来讲,每一行用new分配内存的代码,使用原始指针而不是使用智能指针保存指针的代码,都应该有对应的使用delete的一行代码来释放同样的内存。

        推荐在释放了内存之后,将指针赋一个nullptr的值。这样的话,就可以避免使用已经被释放了内存的指针。也需要指出的是,对于nullptr指针调用delete也是被允许的,只不过它啥也不做而已。

  • 13
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王俊山IT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值