问题描述
我对 C++ 比较陌生,我想同时了解内存管理和指针。
int* p1;
int* p2;
int* p3 = new int[some size];
p1 = p3;
std::swap(p1,p2);
如何正确删除动态分配的内存?做 delete[] p3
就够了吗?之后我也应该删除 p2
吗?
解决方法
当你做这样的事情时,口语中有一些模糊:
delete x;
我们说“我们删除 x
”。严格来说是错误的,因为删除的是x
指向的对象。
通过 new
/new[]
分配的每个对象都必须通过对 delete
/delete[]
的一次调用销毁。无论您有两个或多个指向同一对象的指针都不会改变这一点。
int* p1 = nullptr;
int* p2 = nullptr;
int* p3 = new int[some size]; // p3 points to the array
p1 = p3; // p1 points to the same array
std::swap(p1,p2); // now p1 == nullptr,p2 points to the array
请注意,在您的示例中,指针未初始化。读取它们的值会导致未定义的行为。由于这不是问题的关键,我通过初始化它们来避免这一点。
通过 new []
创建了一个数组,而您必须通过 delete []
删除该数组。您不能将其删除两次。所以要么调用 delete[] p3;
要么调用 delete[] p2;
,但不能同时调用。
PS: 评论已经提到了智能指针,我也建议您阅读它们。现在你不应该使用原始的拥有指针。拥有指针是您需要调用 delete
的指针,它“拥有”指向的对象。原始指针应该只用于“观察”,即您永远不必担心在原始指针上调用 delete
(或 delete[]
)。当然你仍然需要注意所指向的对象是否还活着,但这不是动态分配特有的:
int* p;
{
int x = 42;
p = &x; // p points to x;
} // x goes out of scope
// here p is not a valid pointer anymore
,
要了解这里发生的事情,添加一些调试语句会有所帮助,即:
std::cout << p1 << " " << p2 << " " << p3 << "\n";
跟踪将产生如下输出:
0 0 0x15e7eb0
0x15e7eb0 0 0x15e7eb0
0 0x15e7eb0 0x15e7eb0
(注意我将 p1 和 p1 初始化为 nullptr
)
p3 最初指向一些内存。在赋值 p1 = p3
之后,p1 现在指向与 p3 相同的内存地址。当您交换指针时,现在是 p2 指向与 p3 相同的内存地址。
这里有几点需要注意:
- 您必须将
delete[]
与相应的new[]
配对(不要将delete
与new[]
等调用) - 对已删除的对象调用 delete 是未定义的行为
- 在空指针上调用 delete 是完全安全的
如您所见,处理原始指针和内存分配很容易导致陷阱。通常建议使用智能指针,或者如果您有一个非拥有指针,则使用 observer_ptr
之类的抽象来清楚地指示代码中指针的用途。