当前位置: 首页 > 知识库问答 >
问题:

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

滕无尘
2023-03-14

我有一些类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的位置是哪里?在getphaA()中,或者在赋值之前但在getphaA()中创建对象之后的main函数中?

所有的编辑:我试着把我的问题放下一个小时,但是因为我不知道在C语言中移动/复制语义学,这对我来说很难。

共有1个答案

巫朝明
2023-03-14

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

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

b1=<temporary object>

<temporary object gets destroyed>

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

但是等等,还有更多!b1是原始对象的完美逐位副本,带有绑定函数,然后被销毁。因此,当b1被销毁时,该函数会再次被调用。

这是三位一体规则的间接结果。当一个对象拥有一个资源,并且必须保持对该资源的独占所有权时,您需要提供一个复制和/或移动构造函数和赋值运算符,以准确说明在这种情况下应该发生什么。

注:这些规则随着C17的保证副本省略而略有变化,但基本概念仍然是一样的。

 类似资料:
  • 基本问题:程序何时在C中调用类的析构函数方法?有人告诉我,每当对象超出范围或受到时都会调用它 更具体的问题: 1)如果对象是通过指针创建的,并且该指针后来被删除或给定一个新的地址来指向,它所指向的对象是否调用其析构函数(假设没有其他东西指向它)? 2) 接下来是问题1,什么定义了对象何时超出范围(与对象何时离开给定的{block}无关)。换句话说,什么时候对链表中的对象调用析构函数? 3) 你想手

  • 我有这个代码,输出如下: 链接到下面的示例https://godbolt.org/z/z8Pn9GsTv 输出: 但交换析构函数的位置会产生其他结果: 代码链接https://godbolt.org/z/vxj7dPqaj 输出: 想知道这是一个错误吗?

  • 我是C++的新手,我写了一个小程序来了解赋值如何处理对象。这个页面(http://www.cplusplus.com/doc/tutorial/classes2/)的cpp文档提示我这样做。在这一页上,它指出: 隐式版本[复制赋值操作符]执行浅层复制,这适用于许多类,但不适用于具有指向对象的指针的类,这些对象处理其存储。在这种情况下,不仅类会冒两次删除指向对象的风险,而且赋值会通过在赋值之前不删除

  • 在 Go 语言开篇中我们已经知道,Go 语言与 C 语言之间有着千丝万缕的关系,甚至被称之为 21 世纪的C语言。 所以在 Go 与 C 语言互操作方面,Go 更是提供了强大的支持。尤其是在 Go 中使用 C,你甚至可以直接在 Go 源文件中编写 C 代码,这是其他语言所无法望其项背的。 格式: 在 import "C" 之前通过单行注释或者通过多行注释编写C语言代码 在 import "C" 之

  • 上这个简单的课... JLS的第8.8.9节指出,“如果一个类不包含构造函数声明,那么默认构造函数将被隐式声明。”它还说,只要我们不在类中,那么“默认构造函数只是调用没有参数的超类构造函数。” 因此,因为类扩展了,我们被迫通过调用的构造函数,作为隐式创建的默认构造函数的一部分。 同样地... 即使我们显式地为Gen声明构造函数,JLS的第8.8.7节规定“如果构造函数体不是以显式构造函数调用开始,

  • 更新1:根据建议添加了打印“this”。 更新2:拆分成几个文件,尝试阻止gcc优化。 更新3:记录复制构造函数并输入添加函数。 更新4:在main中添加了Clang和第二个cout的输出。 我希望参数析构函数作为函数中的最后一条语句被调用。从今以后,我希望下面的代码能够提供以下输出。 使用MSVC(Visual Studio)时,输出与预期的一样。但GCC(4.8.2-19ubuntu1)输出以