【发布时间】:2022-06-10 18:22:14
【问题描述】:
如果你的类的移动构造函数是 noexcept,那么 std::vector 将分配新的内存,然后移动在新的内存中构造你的对象。如果它不是“noexcept”,它将复制构造它们。如果它复制构造它们,那么在释放旧缓冲区之前仍然需要销毁这些对象。但是,为什么如果对象被移动,它仍然会调用所有旧对象的析构函数。我不明白为什么这是必要的。除了一些条件检查之外,对移出对象的任何析构函数调用都不会做任何事情。您可能会争辩说“对象在技术上没有被破坏”,这是真的,但是由于该内存正在被释放,并且下次使用该内存时,唯一定义的访问对象的方法是首先构造它们,我不不明白为什么需要这样做:
struct Foo
{
void* buffer;
Foo() : buffer(new char[16]) {}
Foo(Foo&& other) { buffer = other.buffer; if (other.buffer != nullptr) other.buffer = nullptr; }
~Foo()
{
if (buffer != nullptr) delete buffer;
}
};
int main()
{
Foo foo;
Foo foo2 = std::move(foo);
foo.~Foo(); /* NO EFFECT */
/* BUT ASSUMING WE DIDN'T CALL THE CONSTRUCTOR, WE JUST CONSTRUCT OVER IT */
new (&foo) Foo{};
/* THEN THE OLD FOO CEASES TO EXIST EVEN IF THE DESTRUCTOR IS NEVER CALLED */
}
这是一个快速程序,显示 std::vector 调用旧的移出对象上的析构函数:
#include <iostream>
struct Foo
{
Foo() {}
Foo(uint32 id) { }
Foo(const Foo& other)
{
std::cout << "Copy constructor called\n";
}
Foo(Foo&& other) noexcept
{
std::cout << "Move constructor called\n";
};
~Foo()
{
std::cout << "Destructor called\n";
}
};
int main()
{
Foo foo;
std::vector<Foo> v;
v.push_back(std::move(foo));
v.push_back(std::move(foo));
v.push_back(std::move(foo));
v.push_back(std::move(foo));
}
【问题讨论】:
-
因为旧对象仍然存在,需要销毁。将移动构造函数添加到语言中时,它们并没有改变析构函数的工作方式。
-
@Zebrafish 移动的对象保持未指定但有效的状态。它们必须被销毁。
-
你的
Foos 析构函数不需要为移动的对象做一些事情,但这并不意味着一般情况下就是这样
标签: c++ destructor