首页 > 编程语言 > 详细

C++动态内存new和delete(超详细)

时间:2020-03-03 23:21:53      阅读:80      评论:0      收藏:0      [点我收藏+]

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; // 删除具体的指针

数组的动态内存分配

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

假设我有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(超详细)

原文:https://www.cnblogs.com/scyq/p/12405064.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!