【发布时间】:2011-05-04 04:22:19
【问题描述】:
我对使用 C++0x 移动语义移动对象之后的状态感到困惑。我的理解是,一旦一个对象被移动了,它仍然是一个有效的对象,但是它的内部状态已经改变了,所以当它的析构函数被调用时,没有资源被释放。
但如果我的理解是正确的,移动对象的析构函数应该仍然被调用。
但是,当我执行一个简单的测试时,这不会发生:
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的移动构造函数是 调用,移动f1中的所有资源 到f2。 - 现在
f2的析构函数被调用, 释放所有资源。 - 现在
f1的析构函数被调用, 这实际上并没有做任何事情 因为所有资源都被转移了 到f2。不过,析构函数是 尽管如此。
但由于只调用了一个析构函数,因此第 4 步或第 5 步都不会发生。我从析构函数进行了回溯,以查看它是从哪里调用的,它是从第 5 步调用的。那么为什么不调用f2 的析构函数呢?
编辑:好的,我修改了这个,所以它实际上是在管理一个资源。 (一个内部内存缓冲区。)不过,我得到了与只调用一次析构函数相同的行为。
【问题讨论】:
-
你在哪个编译器上测试?
-
gcc 4.3 ....................
-
在这种情况下,值得注意的是它是针对更旧版本的 C++0x 草案编写的。从那时起,移动语义已经发生了很大变化。例如,我相信在您添加
std::move之前,较新的编译器会完全拒绝您的代码。它无法调用work,因为该参数实际上是一个左值引用,因为它被命名了。 -
@jalf,好的。但是你同意我的观点吗,在当前的 C++0x 草案中,析构函数应该在这里调用两次?
-
@Channel72:假设移动没有被优化掉,是的。
标签: c++ c++11 move-semantics