C++动态内存是C++灵活、炫酷的一种操作。学好它,能让自己编程逼格上一个level。
在学习动态内存之前,我们先要了解C++是怎么划分内存的:
这里的栈和堆和数据结构中的栈和堆不是一个概念,不要搞混。内存在物理上其实都是相同的,所以可以说,栈内存和堆内存是系统抽象出来的。
栈本身的空间很小,但是读取速度很快,仅次于寄存器读取。栈上的变量在函数(方法)执行完后,就会被回收。
堆是程序运行过程中,程序自己向系统申请的,申请和销毁都需要时间,堆内存分配存储会花更多时间。所以堆的效率明显低于栈。但是堆的优点在于,编译器不必知道要从堆里分配多少内存空间,也不必知道存储的数据要在堆里停留多长的时间,因此用堆保存数据时会得到更大的灵活性。
那么看到这里,再结合今天的标题,我们可以知道,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; // 会调用类的析构函数
原文:https://www.cnblogs.com/scyq/p/12405064.html