忽略编程风格和设计,在堆栈上分配的变量上调用 delete 是否“安全”?
例如:
int nAmount;
delete &nAmount;
或者
class sample
{
public:
sample();
~sample() { delete &nAmount;}
int nAmount;
}
忽略编程风格和设计,在堆栈上分配的变量上调用 delete 是否“安全”?
例如:
int nAmount;
delete &nAmount;
或者
class sample
{
public:
sample();
~sample() { delete &nAmount;}
int nAmount;
}
不delete
,调用堆栈分配的变量是不安全的。你应该只调用delete
由new
.
malloc
or calloc
,都应该有一个free
。 new
应该有一个delete
。 new[]
应该有一个delete[]
。 通常,您不能混合和匹配其中的任何一个,例如 no free
-ing 或delete[]
-ingnew
对象。这样做会导致未定义的行为。
好吧,让我们尝试一下:
jeremy@jeremy-desktop:~$ echo 'main() { int a; delete &a; }' > test.cpp
jeremy@jeremy-desktop:~$ g++ -o test test.cpp
jeremy@jeremy-desktop:~$ ./test
Segmentation fault
所以显然它根本不安全。
请记住,当您使用 new (或 malloc )分配内存块时,实际分配的内存块将大于您要求的内存块。内存块还将包含一些簿记信息,因此当您释放块时,它可以很容易地放回空闲池并可能与相邻的空闲块合并。
当您尝试释放您没有从 new 接收到的任何内存时,该簿记信息将不存在,但系统将按原样运行,结果将是不可预测的(通常是坏的)。
是的,这是未定义的行为:传递给delete
任何不是来自new
UB 的东西:
C++ 标准,第 3.7.3.2.3 节:提供给标准库中提供的一个释放函数的第一个参数的值可以是
null
指针值;如果是这样,并且如果释放函数是标准库中提供的函数,则对释放函数的调用无效。否则,提供给operator delete(void*)
标准库的值应是先前调用标准库中的一个operator new(std::size_t)
或一个返回的值之一operator new(std::size_t, const std::nothrow_t&)
。
未定义行为的后果是未定义的。“什么都没有发生”与其他任何事情一样有效。然而,它通常是“什么都不会立即发生”:释放无效的内存块可能会在随后调用分配器时产生严重的后果。
在 windows 中玩了一下 g++ 4.4 之后,我得到了非常有趣的结果:
在堆栈变量上调用 delete 似乎没有做任何事情。没有错误抛出,但我可以在删除后毫无问题地访问变量。
如果对象在堆中分配,则具有方法的类delete this
会成功删除对象,但如果在堆栈中分配则不会(如果在堆栈中,则不会发生任何事情)。
没有人能知道会发生什么。这会调用未定义的行为,因此实际上任何事情都可能发生。不要这样做。
不,使用 new 分配的内存应该使用 delete 运算符删除,使用 malloc 分配的内存应该使用 free 删除。并且无需释放分配在堆栈上的变量。
天使失去了它的翅膀......你只能调用delete
分配有的指针new
,否则你会得到未定义的行为。
这里的内存是使用堆栈分配的,所以不需要从外部删除它,但如果你已经动态分配
像 int *a=new int()
那么你必须删除 a 而不是删除 &a(a 本身是一个指针),因为内存是从自由存储区分配的。
你已经自己回答了这个问题。delete
只能用于通过new
. 做任何其他事情都是简单明了的未定义行为。
因此,真的没有说会发生什么,从代码正常工作到崩溃到擦除硬盘驱动器的任何事情都是这样做的有效结果。所以请永远不要这样做。
这是 UB,因为您不能对尚未动态分配 new 的项目调用 delete。就是这么简单。
动机:我有两个对象,A
和B
。我知道A
之前必须实例化B
,可能是因为B
需要由A
. 然而,我想破坏A
之前B
。也许我正在编写一个集成测试,我希望服务器 A 先关闭。我该如何做到这一点?
A a{};
B b{a.port()};
// delete A, how?
解决方案:不要A
在堆栈上分配。相反,使用std::make_unique
并保留一个堆栈分配的智能指针,指向堆分配的A
. 这种方式是最不混乱的选择,IMO。
auto a = std::make_unique<A>();
B b{a->port()};
// ...
a.reset()
或者,我考虑将销毁逻辑移出A
' 析构函数并自己显式调用该方法。析构函数只有在之前没有被调用过时才会调用它。