C ++是否需要为每个展示位置调用析构函数?

时间:2011-01-02 22:56:12

标签: c++

我知道放置新调用通常与对析构函数的显式调用相匹配。我的问题是:如果我不需要析构函数(没有代码放在那里,并且没有包含析构函数的成员变量),我可以安全地跳过显式的析构函数调用吗?

这是我的用例:我想为C API编写C ++绑定。在C API中,许多对象只能通过指针访问。而不是创建包含单个指针的包装器对象(这是浪费和语义混淆)。我想要  使用placement new在C对象的地址构造一个对象。 C ++对象在其构造函数或析构函数中不执行任何操作,并且其方法除了委托给C方法之外什么都不做。 C ++对象不包含任何虚拟方法。

我对这个问题有两个部分。

  1. 有什么理由说这个想法在任何生产编译器的实践中都不起作用?

  2. 这在技术上是否违反了C ++语言规范?

6 个答案:

答案 0 :(得分:2)

我认为答案是,如果你的类是POD(它是,如果它在con /析构函数中没有做任何事情,没有虚拟成员函数,并且没有任何非静态数据成员)事情),那么你不需要调用构造函数一个析构函数,它的生命周期只是底层内存的生命周期。您可以像在C中使用结构一样使用它,并且可以调用其成员函数,无论它是否已构造。

答案 1 :(得分:2)

如果我正确理解了你的问题,你在内存中有一个C对象,你想要在现有对象的“顶部”上初始化一个C ++对象。

CppObject* cppobject = new (cobject) CppObject;

虽然没有为旧对象调用析构函数没有问题 - 这是否会导致资源泄漏或其他问题完全取决于旧对象的类型,并且是用户代码问题,而不是语言一致性问题 - 事实上,为新对象重用内存意味着不再可以访问旧对象。

虽然operator new的放置形式必须只返回它给出的地址,但在调用任何构造函数(如果有)之前,没有什么可以阻止新表达式擦除新对象的内存。未根据C ++语言规则初始化的新对象的成员具有未指定的内容,这绝对不等同于曾经存在于内存中的任何旧对象的内容被重用。

如果我理解正确,那么你要做的就是保证不起作用。

答案 2 :(得分:1)

放置new的目的是允许您创建对象池或将多个对象与std :: vector一起在连续的内存空间中对齐。

如果对象是C-structs,那么你不需要new new来做这个,你可以简单地使用C方法来分配基于sizeof(struct Foo)的内存,其中Foo是结构名,如果你分配多个对象,您可能需要将其大小增加到边界以进行对齐。

然而,没有必要在那里放置新对象,你可以简单地将它们存入。

答案 3 :(得分:0)

要回答你的主要问题,是的,你仍然需要调用析构函数,因为其他东西必须发生。

这个想法在任何生产编译器的实践中都没有任何理由吗?

你真的很确定你的C ++对象符合C对象的大小。

这在技术上是否违反了C ++语言规范?

不,但并非所有符合规范的内容都能按您的意愿运作。

答案 4 :(得分:0)

  

我知道放置新调用通常与对析构函数的显式调用相匹配。如果我不需要析构函数(没有代码放在那里,并且没有包含析构函数的成员变量)我可以安全地跳过显式析构函数调用吗?

是。如果我在发布此答案之前不需要飞往纽约,我可以安全地跳过这次旅行吗? :)但是,如果析构函数真的不需要,因为它什么都不做,那么调用它有什么害处?

如果编译器可以找出析构函数应该是无操作,我希望它能消除该调用。如果你没有写一个明确的dtor,请记住你的班级仍然有一个dtor,而有趣的案例 - 这里 - 是否是语言所称的琐碎。

解决方案:在构建它们之前销毁先前构造的对象,即使你认为dtor是无操作的。

  

我想为C API编写C ++绑定。在C API中,许多对象只能通过指针访问。而不是创建包含单个指针的包装器对象(这是浪费和语义混淆)。我想使用placement new来在C对象的地址构造一个对象。

这是布局兼容类和reinterpret_cast的目的。包括一个静态断言(例如Boost的宏,0x static_assert等),如你所愿,检查大小和/或对齐,进行简短的健全性检查,但最终你必须了解你的实现如何规划类。如果需要,大多数提供pragma(或其他特定于实现的机制)来控制它。

最简单的方法是在C ++类型中包含C结构:

// in C header
typedef struct {
  int n;
  //...
} C_A;
C_A* C_get_a();

// your C++
struct A {
  void blah(int n) {
    _data.num += n;
  }

  // convenience functions
  static A* from(C_A *p) {
    return reinterpret_cast<A*>(p);
  }
  static A const* from(C_A const *p) {
    return reinterpret_cast<A const*>(p);
  }

private:
  C_A _data;  // the only data member
};

void example() {
  A *p = A::from(C_get_a());
  p->blah(42);
}

我喜欢将这样的转换封装起来,而不是整个地进行reinterpret_cast,并且更加统一(即比较const和非const的调用站点),因此便利功能。修改类也有点困难,但没有注意到仍然必须支持这种类型的使用。

根据确切的类别,您可以将数据成员公开。

答案 5 :(得分:0)

第一个问题是:你为什么不只使用演员?然后没有问题的放置新做任何事情,显然没有未使用析构函数的问题。如果C和C ++类型与布局兼容,结果将起作用。

第二个问题是:有什么意义?如果你没有虚函数,你没有使用构造函数或析构函数,那么C ++类似乎没有提供任何优于使用C类型的优点:无论如何,你编写的任何方法都应该是全局函数。

我能想象的唯一优势是,如果要隐藏C表示,可以使用包含所有私有成员的类覆盖C对象,并使用方法进行访问。这是你的目的吗? [我认为这是合理的事情]