我知道放置新调用通常与对析构函数的显式调用相匹配。我的问题是:如果我不需要析构函数(没有代码放在那里,并且没有包含析构函数的成员变量),我可以安全地跳过显式的析构函数调用吗?
这是我的用例:我想为C API编写C ++绑定。在C API中,许多对象只能通过指针访问。而不是创建包含单个指针的包装器对象(这是浪费和语义混淆)。我想要 使用placement new在C对象的地址构造一个对象。 C ++对象在其构造函数或析构函数中不执行任何操作,并且其方法除了委托给C方法之外什么都不做。 C ++对象不包含任何虚拟方法。
我对这个问题有两个部分。
有什么理由说这个想法在任何生产编译器的实践中都不起作用?
这在技术上是否违反了C ++语言规范?
答案 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对象,并使用方法进行访问。这是你的目的吗? [我认为这是合理的事情]