C++内存篇(二):静态内存、栈内存、动态内存,new与dele
一、静态内存/栈内存与动态内存
一个对象是一块特定类型的能存储数据的内存空间。在程序执行中一个对象占用一块内存而被创建,而后在某个时间被销毁,这个过程称为它的生命周期。这里把命名了的对象称为变量。
有一些变量在程序运行之前编译器就会为他们分配内存,因为他们占用的内存是可预期的(如某个编译器下int固定占32位),这些变量称为静态变量,我们在静态内存中为他们分配空间。这类对象包括:
- 局部static对象
- 类static数据成员
- 全局变量
这类对象的共同特征是一旦被声明就会直到程序结束时才被销毁。
栈内存用来存放函数内的非static对象,在运行其存在的程序块时才会为它们分配空间,而在离开其作用域时会被销毁。
以上两类都可以在运行前确定一个变量需要的空间大小,而有一些变量只有在运行时才能确定需要多少内存空间,比如说:
//输入数字n并且创建大小为n的数组。
int n;
cin >> n;
int *p = new int[n];
这时候我们就需要动态内存:以动态的大小地分配空间给变量,这是静态内存和栈内存不能做到的。在C++中我们可以使用new和delete来给对象分配内存和向计算机归还内存。
动态内存允许程序员动态地申请所需空间,但也要求他们一旦不需要这些内存资源的时候就归还他们。把内存归还给计算机是非常重要的,否则就会造成无意义的内存占用导致内存泄漏。尽管归还对象占用的内存看起来理所应当,然而,实际上要保证在复杂的程序中程序员没有疏漏地归还内存是非常困难的(更何况有时候程序没有运行到delete的步骤就抛出异常),为此C++采用了智能指针来解决一部分问题。
二、new与delete
new和delete用来直接管理动态内存的申请和释放。注意使用new申请空间创建的对象不论它在哪里被声明(函数体内或者别的什么地方),都会直到它被delete才释放空间。用于动态内存分配的空间称为自由空间(free store)。
new
初始化
在自由空间构造一个对象,并返回指向该对象的指针:
int *p = new int; //对象值未定义
int *p1 = new int(); //使用()进行默认初始化,*p1 = 0
vector<int> *pv = new vector<int>{0,1,2,3,4} //列表初始化
默认情况下,动态分配的对象会被默认初始化——
- 类类型对象将默认用其默认构造函数来初始化
- 内置类型或组合类型的对象值将是未定义的,因为它们没有默认构造函数
所以对于内置类型最好初始化它以免变量存储着未定义值。
当使用()
初始化对象时,可以使用auto
自动读取它的类型:
auto p1 = new auto(a); //指针类型将和a的类型相同
const对象
const int *pci = new const int(1024)
和其他const对象一样必须初始化(如果是类类型能隐式地初始化也可以)。
内存耗尽
当自由空间被耗尽时,new表达式会失败:
int *p1 = new int; //如果分配失败会抛出一个bad_alloc类型异常
int *p2 = new (nothrow) int; //如果分配失败,返回一个空指针。这是一个定位new,nothrow告诉new不要抛出异常
delete
- 传递给delete的指针必须指向动态分配的内存,或是一个空指针
- 释放一块非new分配的内存或多次释放相同指针的行为是未定义的
- 通常情况下,编译器不能分辨一个指针指向的是动态内存还是静态内存,也不能分辨一个指针指向的内存是否被释放了。不合规的delete会产生未知的潜在危害。
- 一个const对象的值不能被改变,但它本身可以被释放
int main(){
int *p1 = new int(0);
delete p1; //释放p1申请的空间
delete p1; //不要这么做,重复delete一个指针是未定义行为
int *p2 = nullptr;
delete p2; //释放空指针也是没有问题的
int i = 0;
int *p3 = &i;
delete p3; //未定义行为,p3指向一个局部变量而不是动态对象
}
指针空悬问题:释放内存后指针虽然无效了,但很多机器上该指针却仍然保存着动态内存的地址。为避免使用无效地址,可以在指针离开作用域前释放掉它所关联的内存,这样之后就没有机会继续使用指针了。如果需要保留这个指针变量,可以在delete后将它置为nullptr。(但是这仍然只能管到这个指针本身,如果同样的内存值之前赋给了其他指针则仍然无法避免风险,而查找所以指向相同内存的指针是非常困难的)
数组操作
int n;
cin >> n;
int *Array = new int[n]; //创建一个大小为n的int数组,此时Array是指向数组的第0个元素的指针
delete []Array; //释放整个数组空间时要使用[],如果写成delete Array这是一个未定义行为