删除与显式调用析构函数的区别

3
在C++中,使用new分配的对象调用delete会调用类的析构函数并释放内存。如果不使用delete删除对象,而是显式调用其析构函数然后释放内存,是否会有任何区别?
例如,考虑以下示例。
#include <iostream>

struct A {
  int i;
  ~A() { std::cout << "Destructor, i was " << i << std::endl; }
};

int main()
{
  A* p = new A{3};
  p->~A();
  free(p);

  return 0;
}

使用 g++ 7.3.0 和 clang 6.0.1 编译代码,并使用 -Wextra -Wall -pedantic 标志,不会引发任何警告。 程序的输出如预期所示。

Destructor, i was 3

使用valgrind/memcheck运行程序时出现了不匹配的free/delete错误:

==27743== Memcheck, a memory error detector
==27743== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==27743== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==27743== Command: ./destructor
==27743== 
Destructor, i was 3
==27743== Mismatched free() / delete / delete []
==27743==    at 0x4C3033B: free (vg_replace_malloc.c:530)
==27743==    by 0x4009FC: main (in /tmp/destructor)
==27743==  Address 0x5bbac80 is 0 bytes inside a block of size 4 alloc'd
==27743==    at 0x4C2F77F: operator new(unsigned long) (vg_replace_malloc.c:334)
==27743==    by 0x4009DA: main (in /tmp/destructor)
==27743== 
==27743== 
==27743== HEAP SUMMARY:
==27743==     in use at exit: 0 bytes in 0 blocks
==27743==   total heap usage: 3 allocs, 3 frees, 73,732 bytes allocated
==27743== 
==27743== All heap blocks were freed -- no leaks are possible
==27743== 
==27743== For counts of detected and suppressed errors, rerun with: -v
==27743== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

然而,这里没有内存泄漏。

我当然知道在上面的代码中处理对象p的标准方法是调用delete。 我的问题更多是形式上的:

  • delete是否完全等同于调用析构函数和free?标准是否指定了这一点,或者我在上面的代码中遇到了UB(未定义行为)?

如果传递给 free() 的指针不是由 malloc()calloc()realloc() 返回的非空指针,则会产生未定义的行为。因此,您调用 free() 的行为是未定义的。 - Peter
1
@DanielLangr 我不认为这是一个重复的问题。这个问题意识到了析构函数,但是询问析构函数+freedelete的区别。那是不同的... - m8mble
如果问题已经在其他地方得到回答,尽管其他问题的提问方式不同,那么在我看来它是一个重复的问题。虽然我们可能会对此争论 :). 但是答案是相同的。顺便说一下,这两个问题都涉及到完全相同的问题。不调用析构函数不一定是一个问题;例如,请参见在放置new分配的对象上不调用析构函数是否可以? - Daniel Langr
@m8mble 但这也适用于另一个问题,对吧?我能看到两个问题之间唯一的区别是在这个问题中析构函数被显式调用。这并不会改变答案。 - Daniel Langr
显示剩余5条评论
1个回答

0

虽然显式调用析构函数是合法的,但你想这样做的情况非常罕见(例如 std::vector,在其中对象在更大的缓冲区内构造和销毁)。

请注意,您应始终将分配器与适当的内存释放(malloc/free、new/delete 等)相匹配。虽然 operator new() 通常依赖于 malloc,但并没有要求它这样做,如果不匹配,则会导致未定义的结果。


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