Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/147.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 与move构造函数配对时发生意外的析构函数调用_C++_Visual C++_C++11_Visual Studio 2012_Move Semantics - Fatal编程技术网

C++ 与move构造函数配对时发生意外的析构函数调用

C++ 与move构造函数配对时发生意外的析构函数调用,c++,visual-c++,c++11,visual-studio-2012,move-semantics,C++,Visual C++,C++11,Visual Studio 2012,Move Semantics,以下代码是在Visual Studio 2012 Express for Windows Desktop中编译和运行的,作为学习练习 #include <cstdio> class X { public: X() { printf("default constructed\n"); } ~X() { printf("destructed\n");} X(const X&) { printf("copy constructed\n"); }

以下代码是在Visual Studio 2012 Express for Windows Desktop中编译和运行的,作为学习练习

#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();
}
这表明命名的返回值优化已经省略了对move构造函数的调用,并掩盖了潜在的问题

实验2:禁用优化并注释掉move构造函数。生成的输出是我所期望的

default constructed
copy constructed
destructed
destructed

A中的X在超出范围时被销毁

A返回一个临时对象(由move构造函数从X构造),它是一个单独的实例。这在调用方的作用域中被销毁。这将导致再次调用析构函数(临时调用)

之所以选择移动构造函数,是因为编译器检测到X将立即被销毁。要使用这种方法,移动构造函数应该使原始对象中的任何数据无效或重置,以便析构函数不会使移动目标接收的任何数据无效

当您通过值传递右值,或通过值从函数返回任何内容时,编译器首先获得删除副本的选项。如果副本未被省略,但所讨论的类型具有移动构造函数,则编译器需要使用移动构造函数

当您退出创建临时对象的作用域时,它将被销毁。如果引用绑定到临时对象,则当引用超出范围时,临时对象将被销毁,除非由于控制流中断而提前销毁

RVO可以产生与非优化版本不同的行为:

返回值优化,或者简单的RVO,是一种编译器优化技术,包括消除为保持函数返回值而创建的临时对象。(1)在C++中,特别值得注意的是,允许改变所得到的程序的可观察行为。[2 ] < /P>


A中的X在超出范围时被销毁

A返回一个临时对象(由move构造函数从X构造),它是一个单独的实例。这在调用方的作用域中被销毁。这将导致再次调用析构函数(临时调用)

之所以选择移动构造函数,是因为编译器检测到X将立即被销毁。要使用这种方法,移动构造函数应该使原始对象中的任何数据无效或重置,以便析构函数不会使移动目标接收的任何数据无效

当您通过值传递右值,或通过值从函数返回任何内容时,编译器首先获得删除副本的选项。如果副本未被省略,但所讨论的类型具有移动构造函数,则编译器需要使用移动构造函数

当您退出创建临时对象的作用域时,它将被销毁。如果引用绑定到临时对象,则当引用超出范围时,临时对象将被销毁,除非由于控制流中断而提前销毁

RVO可以产生与非优化版本不同的行为:

返回值优化,或者简单的RVO,是一种编译器优化技术,包括消除为保持函数返回值而创建的临时对象。(1)在C++中,特别值得注意的是,允许改变所得到的程序的可观察行为。[2 ] < /P>


请记住,当对象是移动操作的源时,它仍将被销毁。因此,移动源需要将其自身置于这样一种状态,即被破坏不会释放它不再拥有的资源(因为它们已被移动到另一个对象)。例如,源对象中的任何原始指针(现在将由移动构造的对象拥有)都应设置为NULL。

请记住,当对象是移动操作的源时,它仍将被销毁。因此,移动源需要将其自身置于这样一种状态,即被破坏不会释放它不再拥有的资源(因为它们已被移动到另一个对象)。例如,源对象中的任何原始指针(现在将由move构造的对象拥有)都应设置为NULL。

尽管Michael和jspcal的答案是准确的,但它们没有回答我问题的核心,这就是为什么会有两个析构函数调用。我只期待一个

答案是函数A()返回一个临时对象。总是。这就是函数返回值的工作方式,而移动语义与此事实无关。我猜Michael和jspcal认为我没有错过这样一个基本事实。我将“移动”一词等同于“交换”的概念。交换时,不会构造和销毁对象。因此,我只期待一个析构函数调用

由于返回的对象必须构造和析构函数,因此进行了第二次析构函数调用(和第二次构造函数调用)


现在,选择要执行的实际构造函数取决于类定义中提供的内容。如果移动构造函数可用,则将调用该构造函数。否则将调用复制构造函数

虽然Michael和jspcal的回答是准确的,但他们没有回答我问题的核心,这就是为什么有两个析构函数调用。我只期待一个

答案是函数A()返回一个临时对象。总是。这就是函数返回值的工作方式,而移动语义与此事实无关。我猜Michael和jspcal认为我没有错过这样一个基本事实。我将“移动”一词等同于“交换”的概念。交换时,不会构造和销毁对象。因此,我只期待一个析构函数调用

因为返回的对象必须被构造和分解
default constructed
destructed
default constructed
copy constructed
destructed
destructed