全局对象在程序启动时分配,在程序结束时销毁。
局部自动对象,当我们进入其定义所在的程序块时被创建,在离开块时销毁。
局部 static 对象在第一次使用前分配,在程序结束时销毁
静态内存用来保存局部static对象,类static 数据成员,以及定义在任何函数之外的变量。
栈内存用来保存定义在函数内的非static对象。分配在静态或栈内存中的对象由编译器自动创建和销毁。
对于栈对象,仅在其定义的程序块运行时才存在,static对象在使用之前分配,在程序销毁时结束。
除了静态内存和栈内存,每个程序还拥有一个内存池。这部分内存被称为自由空间或堆(heap)。程序用堆来存储动态分配的对象。动态对象不在使用的时候,代码必须显式的销毁它们。
使用new动态分配和初始化对象
在自由空间分配的内存是无名的,因此 new 无法为其分配对象命名,而是返回一个指向该对象的指针
//p 指向一个动态分配的、未初始化的无名对象
int *p = new int;
默认情况下,动态分配的对象是默认初始化的,这意味着内置类型或组合类型的对象的值将是未定义的,而类类型对象将用默认构造函数进行初始化
//初始化为空string
string *ps = new string;
//pi指向一个未初始化的int
int *pi = new int;
我们可以用直接初始化方式来初始化一个动态分配的对象。可以使用传统的构造方式,在c++11中,也可以使用列表初始化
// p 指向的对象的值为1024
int *p = new int(1024);
//ps指向的对象的为‘9999999999’
string *ps = new string(10, '9');
//pv五个元素,值为 0 1 2 3 4
vector<int> *pv = new vector<int>{0, 1, 2, 3, 4};
也可以对动态分配的对象进行值初始化,只需在类型名之后跟一对空括号即可。
string *ps1 = new string; // 默认初始化为空string
string *ps2 = new string() // 值初始化为空string
int *pi1 = new int; // 默认初始化 *pi1值未定义
int *pi2 = new int(); //值初始化为0;*pi2为0
对于定义了自己的构造函数的类类型(例如string)来说,要求值初始化是没有意义的。不管采用什么形式,对象都会通过默认构造函数来初始化,但对于内置类型,两种形式的差别就很大了;值初始化的内置类型有着良好定义的值,而默认初始化的对象的值则是未定的。类似的,对于类中那些依赖于编译器合成的默认构造函数的内置类型成员,那么它们的值也是未定义的。
如果我们提供了一个括号包围的初始化器,就可以使用auto。从此初始化器来推断我们想要分配的对象的类型。但是,由于编译器要用初始化的类型来推断要分配的类型,只有当括号中仅有单一初始化器时才可以使用auto
auto p1 = new auto(obj); // p指向一个与obj类型相同的对象
//该对象用obj进行初始化
auto p2 = new auto(a, b, c); //错误 括号中只能有单个初始化器
动态分配的const对象
用new分配const对象是合法的
//分配并初始化一个const int
const int *pci = new const int(1024);
//分配并默认初始化一个const的空string
const string *pcs = new const string;
类似于其他任何const对象,一个动态分配的const对象必须进行初始化,对于一个定义了默认构造函数的类类型,其const 动态对象可以隐式初始化,而其他类型的对象就必须显式初始化。由于分配的对象是const的,new返回的指针是一个指向const的指针
内存耗尽
一旦一个程序用光了它所有可用的内存,new表达式就会失败,默认情况下,如果new不能分配所要求的空间内存,它会抛出一个类型为 bad_alloc 的异常。我们可以改变使用new的方式来阻止它抛出异常
// 如果分配失败,new返回一个空指针
int *p = new int;
int *p1 = new (nothrow) int; //如果分配失败,new返回一个空指针
我们称这种形式的new为定位new
释放动态内存
为了防止内存耗尽,在动态内存使用完毕之后,必须将其归还给系统,我们用delete 来将动态内存归还给系统。
与new类似,delete表达式也执行两个动作,销毁给定的指针指向的对象,释放对应得内存。