【C++】动态内存(二):动态内存与智能指针——直接管理内存

发布于:2024-12-07 ⋅ 阅读:(135) ⋅ 点赞:(0)

12.1.2 直接管理内存

C++ 定义了两个运算符来分配和释放动态内存。运算符 new 分配内存,delete 释放 new 分配的内存。

相对于智能指针,这两个运算符管理内存非常容易出错。使用智能指针的程序更容易编写和调试。

在学习拷贝控制之前,除非使用智能指针来管理内存,否则不要分配动态内存。

使用 new 动态分配和初始化对象

在自由空间分配的内存是无名的,因此 new 无法为其分配的对象命名,而是返回一个指向该对象的指针:

int *pi = new int;	// pi 指向一个动态分配的、未初始化的无名对象

此 new 表达式在自由空间构造一个 int 型对象,并返回该对象的指针。

默认情况下,动态分配的对象是默认初始化的,这意味着内置类型或组合类型的对象的值将是未定义的,而类类型对象将使用默认构造函数进行初始化:

string *ps = new string;	// 初始化为空 string
int *pi = new int;			// pi 指向一个未初始化的 int

可以使用直接初始化的方式来初始化一个动态分配的对象。可以用传统的构造方式,在 C++ 11 标准下,也可以用列表初始化的方式。

同样地,可以对动态分配的对象进行值初始化。

在 C++ 11 标准下,如果我们提供了一个括号包围的初始化器,就可以用 auto 从此初始化器来推断我们想要分配的对象的类型。但由于编译器要用初始化器的类型来推断要分配的类型,只有当括号中仅有单一初始化器时才可以用 auto。

auto p1 = new auto(obj);	// p 指向一个与 obj 类型相同的对象
							// 该对象用 obj 初始化
auto p2 = new auto{a, b, c}; // 错误❌: 括号中只能有单个初始化器

动态分配的 const 对象

用 new 分配 const 对象是合法的:

const int *pci = new const int(1024);
const string *pcs = new const string;	// 默认初始化一个 const 的空的 string

一个动态分配的 const 对象必须初始化。对于定义了默认构造函数的类类型,const 动态对象可以隐式初始化,而其它类型的对象必须显式初始化。由于分配的对象是 const 的,new 返回的指针是一个指向 const 的指针。

内存耗尽

一旦一个程序用光了所有可用的内存,new 表达式就会失败。默认情况下,如果 new 不能分配所要求的内存空间,就会抛出一个类行为 bad_alloc 的异常。我们可以改变使用 new 的方式来阻止它抛出异常:

int *pi = new int;				// 如果分配失败, new 抛出 std::bad_alloc
int *p2 = new (nothrow) int;	// 如果分配失败, new 返回一个空指针

释放动态内存

为了防止内存耗尽,在动态内存使用完毕后,必须将其归还给系统。通过 delete 表达式 来将动态内存归还给系统。delete 接受一个指针,指向我们要释放的对象。

指针值和 delete

通常情况下,编译器不能分辨一个指针指向的对象是静态的还是动态分配的。类似地,编译器不能分辨一个指针所指向的内存是否已经释放。

动态对象的生存期直到被释放时为止

由内置指针(而不是智能指针)管理的动态内存在被显式释放之前都将会一直存在。

delete 之后重置指针值 …

当 delete 一个指针后,指针值就无效了。虽然指针已经无效,但在很多机器上指针仍然仍然保存着动态内存的地址。delete 之后,指针变为“空悬指针”。和未初始化的指针类似,空悬指针同样是危险的。

安全的做法是在 delete 之后将 nullptr 赋予空悬指针,使它不再指向任何对象。

… 这只提供了有限的保护

动态内存的一个基本问题是可能有多个指针指向相同的内存。在 delete 之后重置指针的方法只对这个指针有效,而其它任何仍指向(已经被释放)的内存的指针是没有作用的。


网站公告

今日签到

点亮在社区的每一天
去签到