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

When is the destructor of an object that is being moved to called in C++?

我有一些 class 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() 的末尾)。

添加移动构造函数和赋值运算符后,一切正常:

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++ 中的 move/copy 语义,这对我来说非常困难。

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

A <temporary object>(std::bind( ... )

b1=<temporary object>

<temporary object gets destroyed>

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

等等,还有更多! b1 是原始对象的完美 bit-by-bit 副本,在它被销毁之前具有绑定功能。因此,当 b1 被销毁时,该函数将再次被调用。

这是Rule Of Three的间接结果。当一个对象拥有一个资源,并且必须保持对该资源的独占所有权时,您需要提供一个副本 and/or 移动构造函数 and 和赋值运算符来准确说明在那种情况下应该发生什么。

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