什么时候在C ++中调用对象的析构函数?

问题描述

我有一个A,我可以不使用它而构造一个std::function。在破坏时,应该调用给定的函数(如果有的话)。我的问题是,该对象在创建并由我的getSomeA()函数返回后被销毁,该函数在应该被调用之前调用std::function。传递给构造函数函数仅应调用一次。一些示例代码

#include <iostream>
#include <functional>

static void testFunction(const std::string& msg)
{
    std::cout << "TestFunction: " << msg << "\n";
}

class A {
public:
    A(void) = default;

    A(const std::function<void()>& onDestroy) :
        onDestroy(onDestroy)
    {  }

    ~A(void)
    {
        if (onDestroy) onDestroy();
        else std::cout << "in dtor but no onDestroy was set\n";
    }

private:
    std::function<void()> onDestroy;
};

A getSomeA(void)
{
    return A(std::bind(testFunction,"the A that was created inside getSomeA"));
}

int main(void)
{
    A b1;
    std::cout << "After creating b1\n";
    b1 = getSomeA();
    std::cout << "After reassigning b1\n";

    std::cout << "Here the program works with b1...\n";
}

程序输出

After creating b1
TestFunction: the A that was created inside getSomeA
After reassigning b1
Here the program works with b1...
TestFunction: the A that was created inside getSomeA

因此该函数在应该被调用之前(在int main()的末尾)被调用

添加了move构造函数和赋值运算符后,所有操作均按预期进行:

A(A&& other) :
        onDestroy(std::exchange(other.onDestroy,nullptr))
    {  }

    A& operator=(A&& other)
    {
        onDestroy = std::exchange(other.onDestroy,nullptr);
        return *this;
    }

程序输出

After creating b1
in dtor but no onDestroy was set
After reassigning b1
Here the program works with b1...
TestFunction: the A that was created inside getSomeA

实际问题:第一次破坏对象在哪里,以便调用testFunction?在getSomeA()或在赋值之前但在getSomeA()中创建对象之后的主函数中?

所有修改:我试图将问题缩短一小时,但由于我不了解C ++中的移动/复制语义,这对我来说很难。

解决方法

在第一个版本中,作为返回getSomeA中构造的对象的一部分,对象首次被销毁。它的return语句有效地构造了一个临时对象,然后将其分配给main的{​​{1}}。如果您忽略从函数返回的过程,则事件的顺序为:

b1

这时,由于临时对象被销毁,因此绑定函数被调用。临时对象是一个完全被欺骗的对象,具有所有权利和特权。包括析构函数。

但是,等等,还有更多! A <temporary object>(std::bind( ... ) b1=<temporary object> <temporary object gets destroyed> 是原始对象的完美逐位副本,具有绑定功能,可以被销毁。因此,b1被销毁后,该函数将再次被调用。

这是Rule Of Three的间接结果。当对象拥有资源,并且必须维护该资源的排他性所有权时,您将需要提供副本和/或移动构造函数 和赋值运算符以准确说明在那种情况下应该发生什么。

P.S。这些规则随着C ++ 17保证的复制省略而略有变化,但是基本概念仍然相同。