【问题标题】:When is the destructor of an object that is being moved to called in C++?何时在 C++ 中调用要移动到的对象的析构函数?
【发布时间】:2021-01-20 19:34:00
【问题描述】:

我有一些类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++ 中的移动/复制语义,这对我来说非常困难。

【问题讨论】:

  • 你能把这个问题简化一下吗?方便我们提供帮助。目前有很多内容要阅读。
  • 好的,我会试试的。
  • A&amp; operator=(A other) 是复制赋值运算符,而不是移动赋值运算符。
  • 移动语义不会改变对象的生命周期,只会管理对象的内部资源。不管它的内容如何,​​如果一个对象在动态内存中,那么当它被调用delete 时它就会被销毁,如果一个对象在自动内存中,那么当它超出范围时它就会被销毁。如果一个对象是临时对象,它会在创建它的语句结束时超出范围。所以,如果你没有正确地实现Rule of 3/5/0,那么当copy-from/moved-from 对象被销毁时,你就会有不好的事情发生。
  • @AsteroidsWithWings 实际上,如果A 实现了复制构造函数和移动构造函数,则该运算符既可以作为复制赋值运算符,也可以作为移动赋值运算符

标签: c++ callback destructor


【解决方案1】:

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

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

b1=<temporary object>

<temporary object gets destroyed>

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

但是等等,还有更多! b1 是原始对象的完美逐位副本,带有绑定函数,在它被破坏之前。所以当b1 被销毁时,函数会再次被调用。

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

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

【讨论】:

    猜你喜欢
    • 2014-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-24
    • 1970-01-01
    • 2015-12-14
    • 2015-08-22
    • 2012-09-26
    相关资源
    最近更新 更多