C++动态内存new和delete

C++动态内存是C++灵活、炫酷的一种操作。学好它,能让自己编程逼格上一个level。

在学习动态内存之前,我们先要了解C++是怎么划分内存的:

  • 栈:在函数内部声明的所有变量都将占用栈内存。栈是由编译器自动分配和释放的,由系统分配。
  • 堆:这是程序中未使用的内存,在程序运行时可用于动态分配内存。大名鼎鼎的GC(Garbage Collection)垃圾回收机制在堆内存上进行的。

这里的栈和堆和数据结构中的栈和堆不是一个概念,不要搞混。内存在物理上其实都是相同的,所以可以说,栈内存和堆内存是系统抽象出来的。

栈本身的空间很小,但是读取速度很快,仅次于寄存器读取。栈上的变量在函数(方法)执行完后,就会被回收。

堆是程序运行过程中,程序自己向系统申请的,申请和销毁都需要时间,堆内存分配存储会花更多时间。所以堆的效率明显低于栈。但是堆的优点在于,编译器不必知道要从堆里分配多少内存空间,也不必知道存储的数据要在堆里停留多长的时间,因此用堆保存数据时会得到更大的灵活性。

那么看到这里,再结合今天的标题,我们可以知道,C++动态内存就是向系统申请堆内存,我们使用new运算符申请,系统将返回所分配的堆内存的地址(这句话很重要)。如果不再需要这块动态分配的内存空间,可以(必须)使用delete运算符,删除之前分配的内存。

具体实现

下面是使用 new 运算符来为任意的数据类型动态分配内存的通用语法:

new data-type;

在这里,data-type 可以是包括数组在内的任意内置的数据类型,也可以是包括类或结构在内的用户自定义的任何数据类型。光分配不行啊,我们还要对这个数据进行操作啊,如下。

假设我们有一个指向int类型的指针,申请内存如下:

int* p = NULL; // 初始化指针,指向空
p = new int; // 为变量请求内存

这里,就可以看出我之前加粗的话的作用了,new返回的是申请分配的堆内存的地址。所以我们在动态内存中,不同于以往要么直接对变量操作、要么利用引用操作,我们都是通过指针来获取地址,在“地址层”对数据进行访问。

此后,你便可以利用p指针来操作堆内存里的数据了。

// ...略
int main(){
int* p = new int;
*p = 520;
cout << "Value of p is: " << *p << endl; delete p; return 0;
}

事实上,new不仅分配了内存,还创建了对象。(可以参考其他语言的内存分配,尤其是Java)

如果我们不再需要使用这个动态分配的内存时,务必要用delete运算符释放它占用的内存,不然会造成十分严重的内存泄漏问题。也就是说new和delete必须配对使用。

delete p; // 删除具体的指针

这里再多说一句,delete的操作,是删除了p所指的目标,释放了它所占的堆空间,而不是删除p本身(指针p本身并没有撤销,它自己仍然存在,该指针所占内存空间并未释放,指针p的真正释放是随着函数调用的结束而消失),释放堆空间后,p成了"空指针"。如果我们在delete p后没有进行指针p的制空(p=NULL)的话,其实指针p这时会成为野指针,为了使用的安全,我们一般在delete p之后还会加上p=NULL这一语句。

所以我们在数据结构中,经常会看见这样的操作:

temp = p;  // temp和p指向同一块堆内存
p = p->right; // p指向别的内存
delete temp; // 删除最初p指向的那块堆内存的数据

数组的动态内存分配

基本数据类型的动态内存分配是很简单很少用的,动态内存更多用在数组、对象的分配上,这里我们先说数组。

假设我有n个苹果,每个苹果的质量不同,要存进数组里,我可能会这么写:

int n = 0;
cin >> n;
int apples[n];

乍一看一点问题没有,但是编译器是不会通过的。我们只能通过动态内存来对未知大小的数组分配空间。

int n;
cin >> n;
int* p = NULL; // 初始化指针
p = new int[n]; // 为变量p申请内存

这样就一点问题都没有了。如果要删除,则

delete [] p;	// 删除p指向的数组

注意一下删除的格式,有点奇怪。下面拿一维数组举例子:

int* array = new int[n];
for (int i = 0; i < n; i++){
cin >> array[i]; // 注意不是 *array[i]!!!
// 或者 cin >> *(array + i);
}
delete [] array;

这里很多人问为什么不是*array[i],应该是把里面的值取出来啊!其实不是,详情可以参见指针与数组的关系

这里简单说下,大家都知道数组名是数组的首地址,如果有int a[10];,我们甚至可以分开理解,就把a看成是这个数组的指针。数组a[2]是取出第三个元素,那么指向数组的指针p同样也是p[2]取出。所以很多时候指针和数组是等同的。

但是,数组作为数组还是有其尊严的。数组的首地址是不可以改变的!上述例子,如果我想通过移动array来操作动态内存中的数组是绝对不可以的,详见数组的禁忌 。所以单纯指向数组的指针ptr,我们可以用ptr++这种操作来实现指针在数组内的游走。但是绝对不可以直接移动数组名这种方式来操作。所以动态内存分配的数组,并不像栈中的数组一样有“实形”,它的地址就是它的本身。所以上述例子中,严禁用改变array的方式来操作动态分配的数组。这样导致了数组首地址的改变。

同时值得说一句,动态数组,如果不进行计数或者开始就知道大小,没有很好的办法知道数组的长度。

对象的动态内存分配

有过其他面向对象语言学习的都知道对象的创建用到了new,如Java的对象创建大致如下。

Animal tiger = new Animal();

现在我们知道了,因为Java的GC机制,tiger这个对象是被创建在堆上的。我们甚至可以说,Java没有栈上的对象。而C++不一样,C++是支持栈上的对象的。我们现在为了防止对象被销毁,需要在堆上创建对象,便采取动态内存。

Animal* p = NULL; // 创建指向Animal类的指针
p = new Animal; // p赋予了新建对象的起始地址

当然,我们也可以调用类的构造函数来创立对象。

Animal* p = new Animal();  // 没有传入参数的构造函数
Animal* p = new Animal("Tiger"); // 传入了参数的构造函数

由于C++没有垃圾回收机制,我们必须在不使用改对象时,删除它。(这下你理解了为什么大家都说C#,Java是自动挡,C++是手动挡车了)

delete p; // 会调用类的析构函数

C++动态内存new和delete(超详细)的更多相关文章

  1. jdk动态代理和cglib动态代理底层实现原理超详细解析(jdk动态代理篇)

    代理模式是一种很常见的模式,本文主要分析jdk动态代理的过程 1.举例 public class ProxyFactory implements InvocationHandler { private ...

  2. c++动态内存管理与智能指针

    目录 一.介绍 二.shared_ptr类 make_shared函数 shared_ptr的拷贝和引用 shared_ptr自动销毁所管理的对象- -shared_ptr还会自动释放相关联对象的内存 ...

  3. C++—动态内存管理之深入探究new和delete

    C++中程序存储空间除栈空间和静态区外,每个程序还拥有一个内存池,这部分内存被称为自由空间(free store)或堆(heap).程序用堆来存储动态分配的对象,即,那些程序运行时分配的对象.动态对象 ...

  4. C++动态内存管理之深入探究new和delete

    C++中程序存储空间除栈空间和静态区外,每个程序还拥有一个内存池,这部分内存被称为自由空间(free store)或堆(heap).程序用堆来存储动态分配的对象,即,那些程序运行时分配的对象.动态对象 ...

  5. C++编程学习(八)new&delete动态内存分配

    前段时间楼主忙着期末大作业,停更了一段,今天刚好在做机器人课程的大作业时,和同组的小伙伴利用python做了工业机器人的在线编程,突然想起来很久没有阅读大型工程了,马上补上- 接下来的几篇博客主要是博 ...

  6. C++ Primer : 第十二章 : 动态内存之动态内存管理(new和delete)

    C++语言定义了两个运算符来分配和释放动态内存:运算符new分配内存,运算符delete释放new分配的内存. 运算符new和delete 使用new动态分配和初始化对象 在自由空间分配的内存是无名的 ...

  7. [008]new、delete及动态内存分配

    1.new和delete都会用,这里只声明一点: C++ 没有明确定义如何释放指向不是用 new 分配的内存地址的指针. 比如下面的代码: #include<iostream> using ...

  8. 动态内存分配(new)和释放(delete)

    在之前我们所写过的程序中,所必需的内存空间的大小都是在程序执行之前就已经确定了.但如果我们需要内存大小为一个变量,其数值只有在程序运行时 (runtime)才能确定,例如有些情况下我们需要根据用户输入 ...

  9. 深入理解C++ new/delete, new []/delete[]动态内存管理

    在C语言中,我们写程序时,总是会有动态开辟内存的需求,每到这个时候我们就会想到用malloc/free 去从堆里面动态申请出来一段内存给我们用.但对这一块申请出来的内存,往往还需要我们对它进行稍许的“ ...

随机推荐

  1. Sequence to Sequence Learning with Neural Networks论文阅读

    论文下载 作者(三位Google大佬)一开始提出DNN的缺点,DNN不能用于将序列映射到序列.此论文以机器翻译为例,核心模型是长短期记忆神经网络(LSTM),首先通过一个多层的LSTM将输入的语言序列 ...

  2. CVPR 2020 全部论文 分类汇总和打包下载

    CVPR 2020 共收录 1470篇文章,根据当前的公布情况,人工智能学社整理了以下约100篇,分享给读者. 代码开源情况:详见每篇注释,当前共15篇开源.(持续更新中,可关注了解). 算法主要领域 ...

  3. PHP把图片存入数据库(非路径)【待测试】

    大部分人的图片上传都是保存一个路径到数据库,这样在插入时确实快,也符合web的特点,但是在删除时就很麻烦,需要找到文件并删除,该代码能够把代码直接存入数据库,删除时一并删除.请注意:这样的话数据库大小 ...

  4. go入门二

    一.流程控制 1.选择结构 if-else: package main import ( "io/ioutil" "fmt" ) func main(){ co ...

  5. 今天建了一个Python学习交流的QQ群,求喜欢python的一起来交流。

    版权归作者所有,任何形式转载请联系作者.作者:枫(来自豆瓣)来源:https://www.douban.com/note/666182545/ 现在学python的人越来越多了,我也开始学习了,大群里 ...

  6. Linux内核文档:如何写符合 kernel-doc 规范的注释

    简介 Linux内核使用 Sphinx 实现把 Documentation 目录下的 reStructuredText 文件转换为非常漂亮的文档.文档既可以通过 make htmldocs 转换成 H ...

  7. JAVA多线程面试必看(转载)

    JAVA多线程和并发基础面试问答 原文链接:http://ifeve.com/java-multi-threading-concurrency-interview-questions-with-ans ...

  8. search(3)- elastic4s-QueryDSL

    elastic4s是elasticsearch一个第三方开发的scala语言终端工具库(Elastic4s is a concise, idiomatic, reactive, type safe S ...

  9. [ctfhub]SQL注入

    今天在ctfhub整理了几个sql注入的解题过程,还算是比较详细的. 知识点都是比较常见的:每个题大致涉及的知识点用一张表格解释 !注:下方的 information_schema.xxxxxxxxx ...

  10. C 2015年真题【保】

    1.编写一个完整的程序,使之能完成以下功能:从键盘中输入若干个整数,用链表储存这些输入的数,并要求存储的顺序与输入的顺序相反. 分析:链表建立[头插法] 代码: #include <stdio. ...