在堆栈上分配的变量上调用delete

时间:2009-01-14 03:41:59

标签: c++ stack heap delete-operator

忽略编程风格和设计,在堆栈上分配的变量上调用delete是否“安全”?

例如:

   int nAmount;
   delete &nAmount;

class sample
{
public:
    sample();
    ~sample() { delete &nAmount;}
    int nAmount;
}

10 个答案:

答案 0 :(得分:96)

No,在堆栈分配的变量上调用delete是不安全的。您只应对delete创建的内容致电new

  • 对于每个malloccalloc,应该只有一个free
  • 对于每个new,应该只有一个delete
  • 对于每个new[],应该只有一个delete[]
  • 对于每个堆栈分配,不应该有明确的释放或删除。在适用的情况下,会自动调用析构函数。

一般情况下,您不能混用和匹配任何这些内容,例如:没有free - 或delete[] - new个对象。这样做会导致未定义的行为。

答案 1 :(得分:46)

好吧,让我们试一试:

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

显然它根本不安全。

答案 2 :(得分:14)

请记住,当您使用new(或malloc)分配内存块时,分配的实际内存块将大于您的要求。 内存块还将包含一些簿记信息,这样当您释放块时,它可以很容易地放回到空闲池中,并可能与相邻的空闲块合并。

当你试图释放你没有从新收到的任何记忆时,那个记账信息就不会存在,但是系统会像现在这样行事,而且结果将是不可预测的(通常很糟糕)。

答案 3 :(得分:10)

是的,它是未定义的行为:传递给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&)返回的值之一。

未定义行为的后果是未定义的。 “没有任何事情发生”与其他任何事情一样有效。但是,它通常“没有立即发生”:取消分配无效的内存块可能会对后续调用分配器产生严重后果。

答案 4 :(得分:7)

在Windows中使用g ++ 4.4后,我得到了非常有趣的结果:

  1. 在堆栈变量上调用delete似乎没有做任何事情。没有错误抛出,但删除后我可以毫无问题地访问变量。

  2. 如果一个带有delete this的方法的类成功删除了该对象(如果它是在堆中分配的话),而不是它是否在堆栈中分配(如果它在堆栈中,则没有任何反应)

答案 5 :(得分:5)

没人知道会发生什么。这会调用未定义的行为,所以任何事情都可能发生。 不要这样做。

答案 6 :(得分:4)

没有, 应使用delete运算符删除使用new分配的内存 使用malloc分配的应该使用free删除。 并且无需释放在堆栈上分配的变量。

答案 7 :(得分:1)

这里使用堆栈分配内存,所以不需要在exernally中删除它,但如果你已经动态地删除了

像 int * a = new int()

然后你必须删除一个而不是删除& a(一个本身就是一个指针),因为内存是从免费商店分配的。

答案 8 :(得分:0)

您已经自己回答了这个问题。 delete必须仅用于通过new获得的指针。做其他事情是简单明了的未定义行为。

因此,实际上没有任何说法会发生什么,从代码工作正常到崩溃到擦除硬盘的任何事情都是这样做的有效结果。所以请永远不要这样做

答案 9 :(得分:0)

动机:我有两个对象,AB。我知道 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 的析构函数并自己明确调用该方法。析构函数只有在之前没有被调用时才会调用它。