我对使用 C++0x move 语义 move 对象之后的状态感到困惑。我的理解是,一旦一个对象被 move ,它仍然是一个有效的对象,但它的内部状态已经改变,因此当它的析构函数被调用时,没有资源被释放。
但如果我的理解是正确的, move 对象的析构函数应该仍然被调用。
但是,当我执行一个简单的测试时,这并没有发生:
struct Foo
{
Foo()
{
s = new char[100];
cout << "Constructor called!" << endl;
}
Foo(Foo&& f)
{
s = f.s;
f.s = 0;
}
~Foo()
{
cout << "Destructor called!" << endl;
delete[] s; // okay if s is NULL
}
void dosomething() { cout << "Doing something..." << endl; }
char* s;
};
void work(Foo&& f2)
{
f2.dosomething();
}
int main()
{
Foo f1;
work(std::move(f1));
}
这个输出:
Constructor called!
Doing something...
Destructor called!
注意析构函数只被调用一次。这表明我对这里的理解是错误的。为什么析构函数没有被调用两次?以下是我对应该发生的事情的解释:
Foo f1
构造完成。Foo f1
被传递给work
,它 采用右值f2
。Foo
的 move 构造函数是 调用, movef1
中的所有资源 到f2
。- 现在
f2
的析构函数被调用, 释放所有资源。 - 现在
f1
的析构函数被调用, 这实际上并没有做任何事情 因为所有资源都转移了 到f2
。不过,析构函数是 尽管如此。
但由于只调用了一个析构函数,因此第 4 步或第 5 步不会发生。我对析构函数进行了回溯,以查看它是从哪里调用的,它是从第 5 步开始调用的。那么,为什么不调用 f2
的析构函数呢?
编辑: 好吧,我修改了这个,所以它实际上是在管理资源。 (一个内部内存缓冲区。)尽管如此,我还是得到了析构函数只被调用一次的相同行为。
最佳答案
编辑 (新的正确答案)
抱歉,仔细查看代码,似乎答案要简单得多:
你永远不会调用 move 构造函数。你永远不会真正 move 对象。您只需将右值引用传递给 work
函数,该函数在该引用上调用一个成员函数,它仍然指向原始对象。
原始答案,为后人保存
为了实际执行 move ,您必须在 work
中包含类似 Foo f3(std::move(f2));
的内容。然后你可以在 f3
上调用你的成员函数,这是一个新对象,通过从 f
据我所知,您根本没有理解 move 语义。您只是看到了普通的旧拷贝省略。
要进行 move ,您必须使用 std::move
(或者具体来说,传递给构造函数的参数必须是未命名/临时的)右值引用,例如从 std::move
返回)。否则,它会被视为普通的老式左值引用,然后 应该 进行复制,但像往常一样,允许编译器优化它,留下一个正在构造的对象,还有一个对象被销毁。
无论如何,即使有 move 语义,编译器也没有理由不做同样的事情:只是优化 move ,就像它已经优化掉拷贝一样。 move 是便宜的,但是只在需要它的地方构造对象,而不是构造一个对象,然后将它 move 到另一个位置并在第一个位置调用析构函数,它仍然更便宜。
同样值得注意的是,您使用的是一个相对较旧的编译器,并且规范的早期版本对于这些“僵尸对象”应该发生什么非常不清楚。所以 GCC 4.3 可能只是不调用析构函数。我相信这只是最后一次修订,或者可能是之前的修订,明确要求调用析构函数
关于c++ - std::move 之后的僵尸对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4082065/