C++定义了2个运算符来分配和释放动态内存。new分配内存,delete释放内存。
1. 使用new动态分配和初始化对象
在*空间分配的内存是无名的,new返回一个指向分配的对象的指针。
int *pi = new int; // pi指向一个动态分配的、未初始化的无名对象
默认情况下,动态分配的对象是默认初始化,内置类型(int, double)或组合类型(struct)的对象的值是未定义的,十分危险。类类型(vector、string)对象使用默认构造函数进行初始化。
string *ps = new string; // string初始化为空
int *pi = new int; // pi指向一个未初始化的int
使用圆括号对对象进行初始化
int *q1 = new int(1024); // q1指向的对象的值为1024
string *ps = new string(10, '9'); // 初始化为"9999999999"
使用列表初始化(花括号)
vector<int> *pv = new vector<int>{0,1,2,3,4,5,6,7,8,9};
对动态分配的对象进行值初始化,只需在类型名之后跟一对空括号即可
string *ps1 = new string; // 默认初始化为空string
string *ps2 = new string(); // 值初始化为空string
int *pi1 = new int; // 默认初始化,*pi1的值未定义
int *pi2 = new int(); // 值初始化为0, *pi2为0
对于定义了自己的构造函数的类类型来说,对象都会通过默认构造函数进行初始化。而内置类型则不具备这个功能,需要我们对其初始化。
若我们提供了一个括号包围的初始化器,则可以使用auto根据初始化器来自动推断我们想分配的对象的类型。由于编译器要用初始化器的类型来推断要分配的类型,只有当括号中仅有单一初始化器时才可以使用auto:
auto p1 = new auto(pi1); // 与pi1类型相同的对象,该对象用pi1进行初始化,p1是int *
auto p2 = new auto(pi1,pi2); // ERROR:括号中只能由单个初始化器
动态分配的const对象必须初始化
用new分配const对象。
const int *pci = new const int(1024); // 分配并初始化一个const int
const string *pcs = new const string;
若系统内存耗尽,则内存分配失败,new会抛出bad_alloc的异常。可以使用定位new来阻止抛异常:
int *p3 = new (nothrow) int; // 若分配失败,new返回一个空指针
2、释放动态内存
动态内存使用完毕后,使用delete将内存归还给系统。delete执行2个动作:销毁给定的指针指向的对象;释放对应的内存。传递给delete的指针必须指向动态分配的内存或一个空指针。
delete p3;
const对象的值不能被改变,但是其本身可以被销毁。
const int *pci = new const int(1024); // 分配并初始化一个const int
delete pci; // 释放一个const对象
动态对象的生存期
对于一个由内置指针管理的动态对象,直到被显式释放之前都是存在的。
动态内存的使用存在的三个常见问题:
- 忘记delete内存
- 使用已经释放的对象
- 同一内存多次释放
delete一个指针之后,指针值变得无效,但是很多机器上依然保存着动态内存的地址,指针变成了空悬指针:指向一块曾经保存数据但是现在已经无效的内存的指针。
避免空悬指针:在指针即将离开其作用域之前释放掉关联的内存,若需要保留指针,可以在delete之后将nullptr赋予指针,表明指针不指向任何对象。
使用智能指针可以避免这些问题。
3、shared_ptr和new的结合
若不初始化一个智能指针,则它会初始化为一个空指针,可以用new返回的指针来初始化智能指针。
shared_ptr<double> p4; // shared_ptr指向一个double
接收指针参数的智能指针构造函数是explicit的,不能将一个内置指针转换为智能指针,必须采用直接初始化:
shared_ptr<int> p5 = new int(42); // ERROR
shared_ptr<int> p5(new int(42)); // p2指向一个值为42的int
一个返回shared_ptr的函数不能在其返回语句中隐式转换一个普通指针:
shared_ptr<int> clone(int p){
return new int(p); // error: 隐式转换为shared_ptr<int>
}
shared_ptr<int> clone(int p){
return shared_ptr<int>(new int(p)); // 正确:显式用int*创建shared_ptr<int>
}
一个用来初始化智能指针的普通指针必须指向动态内存,因为智能指针默认使用delete释放它关联的对象,可以将智能指针绑定到一个指向其他类型的资源的指针上。