Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/160.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++ 函数返回的类上的析构函数_C++_Class_G++_Destructor - Fatal编程技术网

C++ 函数返回的类上的析构函数

C++ 函数返回的类上的析构函数,c++,class,g++,destructor,C++,Class,G++,Destructor,我有以下代码: #include <stdio.h> class Foo { public: int a; ~Foo() { printf("Goodbye %d\n", a); } }; Foo newObj() { Foo obj; return obj; } int main() { Foo bar = newObj(); bar.a = 5; bar = newObj(); } 打印的数字似乎是随机的 我

我有以下代码:

#include <stdio.h>

class Foo {
    public:
    int a;
    ~Foo() { printf("Goodbye %d\n", a); }
};

Foo newObj() {
    Foo obj;
    return obj;
}

int main() {
    Foo bar = newObj();
    bar.a = 5;
    bar = newObj();
}
打印的数字似乎是随机的

我有两个问题:

  • 为什么析构函数调用了两次
  • 为什么不第一次打印
    5
  • 我来自C语言背景,因此使用了
    printf
    ,我很难理解析构函数,何时调用它们以及如何从函数返回类。

    1)很简单,代码中有两个
    Foo
    对象(在
    main
    newObj
    中),所以有两个析构函数调用。实际上,这是您将看到的最小析构函数调用数,编译器可能会为返回值创建一个未命名的临时对象,如果这样做,您将看到三个析构函数调用。关于返回值优化的规则在C++的历史中发生了变化,所以您可能看到或可能没有看到这种行为。 2) 因为调用析构函数时,
    Foo::a
    的值从来都不是5,在
    newObj
    中它从来都不是5,尽管在
    main
    中它是5,但在到达
    main
    末尾时(也就是调用析构函数时),它已经不是了


    我猜你的误解是你认为赋值语句
    bar=newObj()
    应该调用析构函数,但事实并非如此。在赋值过程中,对象被覆盖,但不会被破坏。

    让我们看看主函数中发生了什么:

    int main() {
        Foo bar = newObj();
    
    这里我们只是实例化一个
    Foo
    ,并用
    newObj()的返回值初始化它。这里没有调用析构函数,原因是:为了快速总结,
    obj
    不是复制/移动到
    bar
    然后析构函数
    obj
    ,而是直接在
    bar
    的存储中构造
    obj

        bar.a = 5;
    
    这里没什么可说的。我们只需将
    bar.a
    的值更改为5

        bar = newObj();
    
    在这里,
    bar
    被复制分配1
    newObj()
    的返回值,然后这个函数调用创建的临时对象被销毁2,这是第一个
    再见
    。此时,
    bar.a
    不再是
    5
    ,而是临时对象的
    a
    中的任何内容

    }
    
    main()
    的末尾,局部变量被销毁,包括
    bar
    ,这是第二个
    bye
    ,由于之前的赋值,它不会打印
    5


    1由于用户定义的析构函数,此处不会发生移动分配,因此不会隐式声明移动分配运算符。

    2正如YSC在注释中提到的,请注意此析构函数调用具有未定义的行为,因为它正在访问一个此时未初始化的
    a
    。带有临时对象的
    bar
    赋值,特别是作为其一部分的
    a
    赋值,出于同样的原因,也有未定义的行为。

    我认为这里的主要混淆之一是对象标识

    bar
    始终是同一个对象。当您将不同的对象分配给
    bar
    时,您不会销毁第一个对象-您可以调用
    操作符=(const&Foo)
    (复制分配操作符)。它是一个可以由编译器自动生成(在本例中就是这样)的函数,只需
    newObj()中的任何内容覆盖
    条.a
    。提供您自己的
    operator=
    以查看/何时发生这种情况(并确认
    a
    在发生这种情况之前确实是
    5


    bar
    的析构函数只调用一次-当函数结束时
    bar
    超出范围时。只有另一个析构函数调用-用于第二个
    newObj()
    返回的临时函数。省略了
    newObj()
    中的第一个临时变量(在这种情况下,语言允许它,因为创建和立即销毁它从来没有真正意义),并首先使用
    newObj()
    的返回值直接初始化
    bar
    ,通过初始化
    a
    返回obj避免未定义的行为导致未定义的行为,因为
    obj.a
    未初始化。如果您更改为
    Foo obj{4}
    例如,然后您将看到该值,以及第二次调用中的
    bar
    obj
    析构函数。您还应该从书本或学习资源中学习。C和C++不适合通过试验来弄清规则,这个问题比你想象的更深入。这是因为实际上应该有三个对析构函数的调用:一个是在每次调用
    newObj
    结束并且本地
    obj
    被析构函数时调用;当
    main
    函数结束并且
    bar
    对象被破坏时,会发生一次。如果我们修改程序以在
    main
    函数中打印“checkpoints”(按原样),我们可以看到对
    newObj
    的第一次调用(在定义中
    Foo bad=newObj();
    )不会调用本地对象
    obj
    的析构函数。对
    newObj
    的第二次调用会导致调用析构函数,当
    bar
    被析构函数时,
    main
    的结尾也是如此。这可能是消除对象的无关复制的一部分。技术上有三个
    Foo
    对象:一个在
    main
    函数中,一个在
    newObj
    的每次调用中。我猜只有两个析构函数被调用是因为复制省略(在
    bar
    的复制构造或赋值中),所以实际上只创建了两个对象。@Someprogrammerdude是的,我没有注意到这一点。我想寓意在于,编译器将创建代码,销毁它创建的所有对象,只要您正确定义析构函数,一切都将正常工作。但是创建的对象的数量不是完全可以预测的
    }