我正在设计一个像这样的自定义 C++ 模板容器:
template <class C>
class Container {
public:
...
void clear() {
// should I call destructor on elements here?
for (i...)
array[i].~C(); // ?
}
~Container() {
delete [] array_;
}
private:
C* array_;
};
我应该在 clear() 中手动调用元素的析构函数吗?如果我不这样做,它们将在稍后容器被销毁时被调用(因为我在析构函数中删除了 [] array_),但这不是预期的行为(我们希望它们在 clear() 内部被销毁,就像 std::vector 确实如此)。 但是,如果我确实调用了析构函数,那 block 内存仍然存在,并且当我将在旧元素(现在已被销毁)之上添加新元素时,将对那些被销毁的元素调用赋值运算符,这可能会导致在未定义的行为中。 假设我有一个类:
class Foo {
public:
Foo() { foo_ = new int; }
~Foo() { delete foo_; } // note I don't explicitly set foo_ to nullptr here
Foo(Foo &&f) {
f.foo_ = std::exchange(foo_, f.foo_); // standard practice in move constructors
}
};
好的,到目前为止这看起来不错,但现在如果我做一个
Container<Foo> c;
c.add(Foo());
c.clear();
c.add(Foo());
当调用 clear() 时,初始 Foo 的析构函数被调用,使其 foo_ 指针悬空。接下来,当添加第二个 Foo 时,临时 R 值与被析构对象的旧内容交换,当 temp 将被销毁时,其析构函数将再次尝试删除相同的悬空指针,这将崩溃。
那么,如何正确地 clear() 容器而不为双重删除留出空间? 我还读到它建议不要在析构函数中将指针设置为 nullptr,以免隐藏潜在的悬挂指针问题。
我对如何处理这个问题有点困惑。
编辑:--------------------
对于模板容器,似乎没有不妥协的方法。 到目前为止,我看到了这些选项,正如其他人指出的那样:
- 彻底删除clear()中的底层数组;这将是 安全但性能不佳。但是我不能使用这个选项,因为我 实现无锁多线程容器。
- 我不会调用 clear() 中元素的析构函数,而是调用默认构造函数:array[i] = C();这似乎 就像迄今为止最好的解决方案 - 除了它仍然意味着额外的 默认构造。
- 在 add() 中使用 placement new - 这似乎适用于复制构造。 但是,移动构造到未初始化的对象中似乎仍然存在问题,因为大多数移动构造函数都是通过指针之间的交换实现的——这会使源对象(移动自)无效。
最佳答案
delete [] array_
将为 array_
的每个元素调用析构函数(假设 C
是具有析构函数的类型)。
在任何给定对象上两次调用析构函数会产生未定义的行为。
这意味着您的 clear()
成员函数不应直接调用数组元素的析构函数。
如果您坚持要有一个单独的 clear()
函数,它不会干扰析构函数的工作,只需将其实现为
void clear()
{
delete [] array_;
array = nullptr; // NULL or 0 before C++11
}
这不会干扰析构函数,因为操作符 delete
对 NULL 指针没有影响。
关于c++ - 在自定义容器中调用析构函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38170687/