当与移动构件配对时,发生意外的析构函数调用 [英] Unexpected destructor call occurs when paired with move constuctor
问题描述
以下代码在Visual Studio 2012 Express for Windows Desktop中编译并运行,作为一个学习练习。
#include < cstdio>
class X
{
public:
X(){printf(default constructed \\\
); }
〜X(){printf(destructed\\\
);}
X(const X&){printf }
X(X&&;){printf(move constructed \\\
); }
X& operator =(const X&){printf(copy assignment operator\\\
); }
};
X A(){
X x;
return x;
}
int main(){
{
A();
}
std :: getchar();
}
当编译时禁用编译器优化(/ Od)时,析构函数被调用两次。这是一个问题,假设只构造一个对象。 为什么析构函数被调用两次?如果类管理自己的资源,这不会是一个问题?
默认构造
移动构造
破坏
破坏<<意外调用
我尝试了几个实验来尝试和解释输出,
实验1:当使用优化(/ O1或/ O2)编译相同的代码时,结果输出为: 默认构造
破坏
b $ b
这表示命名返回值优化已经省略了对move构造函数的调用,并掩盖了底层问题。
实验2:禁用优化并注释掉移动构造函数。
默认构造
构造的拷贝
destructed
destructed
虽然Michael和jspcal的答案是准确的,回答我的问题的核心,这是为什么有两个析构函数调用。我期待只有一个。
答案是函数A()返回一个临时对象。总是。这是函数返回值如何工作,移动语义与这个事实没有关系。我猜Michael和jspcal假设我没有错过这样的基本事实。我把移动一词与交换的概念等同起来。当交换时,对象不被构造和破坏。因此,我期望只有一个析构函数调用。
由于返回的对象必须被构造和析构,所以第二个析构函数调用(和第二个构造函数调用) p>
现在,选择要执行的实际构造函数取决于类定义中提供的内容。如果移动构造函数可用,将被调用。否则将调用复制构造函数。
The following code was compiled and run in Visual Studio 2012 Express for Windows Desktop, as a learning exercise.
#include <cstdio>
class X
{
public:
X() { printf("default constructed\n"); }
~X() { printf("destructed\n");}
X(const X&) { printf("copy constructed\n"); }
X(X&&) { printf("move constructed\n"); }
X & operator= (const X &) { printf("copy assignment operator\n"); }
};
X A() {
X x;
return x;
}
int main() {
{
A();
}
std::getchar();
}
When compiled with compiler optimizations disabled (/Od), the resulting output indicates that the destructor is called twice. This is a problem given that only one object is constructed. Why is the destructor being called twice? Wouldn't this be a problem if the class was managing it own resources?
default constructed
move constructed
destructed
destructed <<< Unexpected call
I tried a couple of experiments to try and explain the output, but ultimately these didn't lead to any useful explanations.
Experiment 1: When the same code is compiled with optimizations enabled (/O1 or /O2), the resulting output is:
default constructed
destructed
which indicates that the Named Return Value Optimization has elided the call to the move constructor, and masked the underlying problem.
Experiment 2: Disabled the optimization and commented out the move constructor. The output generated was what I expected.
default constructed
copy constructed
destructed
destructed
Although Michael's and jspcal answers are accurate, they didn't answer the heart of my question, which was why were there two destructor calls made. I was expecting just one.
The answer is that function A() returns a temporary object. Always. This is how function return values work, and move-semantics has no bearing on this fact. I guess Michael and jspcal assumed that I had not missed such a basic fact. I equated the term "moved" with the concept of "swap". When swapped, objects are not constructed and destructed. Hence I was expecting just one destructor call.
Since the returned object must be constructed and destructed, the second destructor call was made (and a second constructor call).
Now, the actual constructor selected to be executed depends on what is provided in the class definition. If a move-constructor is available, that will be called. Otherwise the copy constructor will be called.
这篇关于当与移动构件配对时,发生意外的析构函数调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!