【问题标题】:Deleting object with private destructor使用私有析构函数删除对象
【发布时间】:2009-10-23 06:54:48
【问题描述】:

如何在下面的代码中允许使用私有析构函数删除对象?我已将实际程序简化为以下示例,但它仍然可以编译和工作。

class SomeClass;

int main(int argc, char *argv[])
{
  SomeClass* boo = 0; // in real program it will be valid pointer
  delete boo; // how it can work?

  return -1;
}

class SomeClass
{
private:
  ~SomeClass() {}; // ! private destructor !
};

【问题讨论】:

  • 有趣..如果我将类的定义移到 main() 上方,则会引发编译器错误。否则,我只会得到一个warning C4150: deletion of pointer to incomplete type 'SomeClass'; no destructor called
  • @Naveen:这是意料之中的。不完整的类型是一个问题。私有析构函数是另一个。每个都有自己的诊断信息。您可以通过移动SomeClass 的定义在两者之间切换。

标签: c++ memory-management private-members


【解决方案1】:

您正在尝试删除不完整类类型的对象。 C++ 标准说在这种情况下你会得到未定义的行为(5.3.5/5):

如果要删除的对象在删除点具有不完整的类类型,并且完整的类具有非平凡的析构函数或释放函数,则行为未定义。

要检测此类情况,您可以使用boost::checked_delete:

template<typename T> 
inline void checked_delete( T* p )
{
    typedef char type_must_be_complete[ sizeof(T)? 1: -1 ];
    (void) sizeof(type_must_be_complete);
    delete p;
}

【讨论】:

  • 这利用了 sizeof() 即使对于空的结构/类也不会返回 0,我已经忘记了。 ISO-IEC-14882 第 9.1 节:“类类型的完整对象和成员子对象应具有非零大小。”这意味着数组中的每个对象都将具有唯一的内存地址。结构可能相同——在 C 中也是如此吗?
  • C 中空结构的大小等于 0。
  • @leander:不完全是。您在 C++ 中看到,将sizeof 应用于不完整的类型只是非法。这就是上面的代码试图通过(void) sizeof ... 部分捕获的内容。该表达式应通过发出 error 来停止在符合标准的编译器上进行编译。但是,如果某些奇怪的编译器碰巧允许 sizeof 对不完整类型(作为扩展)产生 0 结果,那么 typedef ... 部分将捕获这些并强制“负大小数组”失败。所以,这里有一个主要的“陷阱”((void) 部分)和“以防万一”的辅助陷阱(typedef 部分)。
  • @leander:在 C 中情况是一样的:将sizeof 应用于不完整的类型是非法。所以,如果有些 C 编译器在这种情况下返回 0,那是它们自己的扩展,与 C 语言关系不大。
  • @Kirill" struct X {}; 为空,它会在任何兼容的 C 编译器上导致编译错误(或更正式地说,“诊断消息”)。因为,正如我上面所说,C 不允许为空结构。
【解决方案2】:

此代码会导致未定义的行为 (UB)。它是 C++ 中的 UB 到 delete 一个具有非平凡析构函数的不完整类型的对象。在您的代码中,SomeClass 类型在delete 处是不完整的,并且它有一个重要的析构函数。编译器通常会对此发出警告,因为在正式的 C++ 中,这不是违反约束。

所以,严格来说,您的代码不能“工作”。它只是编译并在运行时执行一些undefined

编译器不需要捕捉这个错误。这样做的原因是,如果您的对象具有 trivial 析构函数,这可能会非常好。编译器无法知道这个类型最终会有什么样的析构函数,所以它不能确定这是否是一个错误。

【讨论】:

  • 如果 (1) T 有一个微不足道的析构函数 (2) T 不会重新定义 `delete operator``
【解决方案3】:

因为在调用operator delete 时,SomeClass 类型没有完全声明。

删除这样的指针是未定义的行为,但实际上大多数编译器只会释放内存(如果指针非 NULL)而不调用析构函数。

例如,g++ 会给你一个关于这个问题的警告:

foo.cpp: In function 'int main(int, char**)':
foo.cpp:6: warning: possible problem detected in invocation of delete operator:
foo.cpp:5: warning: 'boo' has incomplete type
foo.cpp:1: warning: forward declaration of 'struct SomeClass'
foo.cpp:6: note: neither the destructor nor the class-specific operator delete will be called, even if they are declared when the class is defined.

【讨论】:

  • “删除这样的指针只会释放内存......” - 不!该行为是UNDEFINED,即对“本质上”发生的情况的任何假设都是无效的。
  • @DevSolar:是的,根据标准它是未定义的。在实践中,大多数编译器都会发生这种情况。我已经更新了答案以反映这一点。
  • 我在依赖行为良好领域之外的任何事物方面有些问题。特别是在同事的代码中。它把我逼上墙。我责怪像你这样的答案。 ( ;-)
猜你喜欢
  • 2021-03-17
  • 2012-09-01
  • 2013-09-30
  • 2013-05-21
  • 2011-11-05
  • 2012-02-05
  • 2021-04-10
  • 2012-04-30
  • 1970-01-01
相关资源
最近更新 更多