使用placement-new销毁对象后析构函数未被调用

13

我完全不知道为什么这个代码不起作用。下面的Function是通过placement new创建的。提供了一个函数来检查是否需要销毁它,如果需要,则手动调用其析构函数。

以下是测试用例,其中似乎从未调用析构函数:

/* Represents a function at runtime */ 
class Function {
public:
  /* Creates an invalid function */
  Function():codeptr(0) { }

  /* Creates a function with the given code pointer */
  Function(void *codeptr):codeptr(codeptr) { }

  /* Frees the function machine code */
  ~Function() {
    if(*this) {
      /* <- I explicitly put a debug output here! */
      destroyLLVMCode(codeptr);
    }
  }

public:
  /* Returns true if the function is valid 
   * (if the code pointer is non-null)
   */
  operator bool() const { return codeptr != 0; }

  /* Destroy this function by calling its destructor */
  void destroy() { ~Function(); }

private:
  void *codeptr;
};

我像下面这样使用它。将以下代码削减到最小,仍然展示问题。在我的真实程序中,当然,内存是从分配器以另一种方式分配的。

#include <new>
#include <cstdlib>

int main() { 
  void *buffer = std::malloc(sizeof(Function));
  Function *f = new (buffer) Function(someExecutableLLVMCode);
  /* more code .. register with symbol tables etc.. */
  f->destroy();
}
你可以看到我在代码行中调用了析构函数~Function()。虽然编译器接受,但实际上它并没有被调用:我通过检查编译器是否确实删除了我提供的LLVM代码(在删除codeptr指向的LLVM代码之前,在析构函数中放入了一些代码,以防Function是有效的)来验证这一点。
后来我发现了原因。你能向我解释一下吗?

这段代码从未以任何方式创建函数,也从未调用任何函数的方法,因此它不会意外地销毁任何函数对象... - Chris Dodd
请添加完整的代码,以便我们查看。 - Arunmu
我只是在使用placement new并在外部代码的某个地方调用destroy函数。我认为这不是很重要。我会把它包含在问题中。等一下。 - Johannes Schaub - litb
10
Johannes在夜晚是Stack Overflow社区的一个乐于助人、高效的成员。而白天,他编写具有“bool”转换功能的代码。 - James McNellis
1
@Johannes:在http://www.artima.com/cppsource/safebool.html上有一个安全布尔习惯用法,我认为你会发现它很有趣 :) - Matthieu M.
显示剩余4条评论
3个回答

24
这是因为在这里,~Function(); 不是语法上的析构函数调用。请改用 this->~Function();~Function(); 被解析为一个运算符 ~ 和在堆栈上创建 Function 对象。 Function 类有一个 operator bool,所以这将被编译。

6
但是如果不是这样的情况,如果您必须编写 Function::~Function(),那么 ~Function() 会被解析为什么?将按位取反应用于临时的 Function 对象吗?我认为那是不会编译通过的。哦,更新:他有一个隐式转换为 bool。约翰尼斯,不要这样做! - Cheers and hth. - Alf
Function::Function()会正常工作。但是你关于`Function()`是正确的。它在神圣的C++标准中的5.3.1/9中有描述。 - Johannes Schaub - litb
1
C++标准12.4规定:[注意:必须始终使用成员访问运算符(5.2.5)编写显式析构函数调用;特别地,在成员函数中的一元表达式 ̃X() 不是显式的析构函数调用(5.3.1)。] - Kirill V. Lyadvinsky
2
@Kirill V. Lyadvinsky:非常感谢您对编译过程的详细解释以及如何调用析构函数的说明。+1! - Nawaz
2
天哪,今天真美好!我刚刚学到了一些有关C++的新知识! - wilhelmtell
显示剩余16条评论

8

将显式的析构函数调用更改为

this->~Function();

目前这个“Function”正在构建一个“Function”,然后调用 ~ 位运算符(合法,因为你有一个转换到 bool 的操作),然后销毁该对象,而不是被调用的对象。


-1

据我回忆,析构函数不能被显式调用。尝试将清理代码从析构函数移动到其他函数中,并调用该函数。


你可以显式地调用析构函数(显式构造函数调用更难讨论,因为根本没有构造函数调用的语法,它在语义层面上,但析构函数调用甚至没有这个术语问题:一切都非常清楚)。 - Cheers and hth. - Alf
我们只能像下面这样显式地调用析构函数: Obj o; o.Obj(); 而不是像“mayBeDestroy”方法中所做的那样。否则它应该会产生编译错误。 否则,必须调用Function::Function(); - Arunmu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接