在调用成员函数启动线程时出现不良的析构函数调用

3

我在主函数中有一个事件循环,在其中我想创建一个对象并在该对象的成员函数上运行一个线程。然而,我注意到对象在线程开始之前就被销毁了。我不明白为什么。

我的代码:

#include <iostream>
#include <thread>

class MyClass {
public:
    MyClass(){
        std::cout << "MyClass constructor is called" << std::endl;
    }
    ~MyClass(){
        std::cout << "MyClass destructor is called" << std::endl;
    }    
    void start(){
        std::cout << "MyClass is starting" << std::endl;
    }
};

int main()
{
        MyClass mine;
        std::thread t(&MyClass::start, mine);
        t.join();
}

输出:

MyClass constructor is called
MyClass destructor is called
MyClass is starting
MyClass destructor is called
MyClass destructor is called

期望的输出:

MyClass constructor is called
MyClass is starting
MyClass destructor is called

1
在记录构造函数/析构函数时,不要忘记复制/移动构造函数(以及可能的赋值操作)。 - Jarod42
@PeteBecker 谢谢你的建议。但请允许我尊敬地不同意。除了我迷惑之处只是在按引用传递和按值传递之间弄混(而不是“对象生命周期”的基本知识),我宁愿(也必须,因为我的工作需要)在使用更高级功能时学习我所遗漏的先决条件。这样的评论并非像我这样的学习者在SO上遇到问题时所寻求的答案。 - narengi
2个回答

10

mine 以引用的方式传递: std::thread t(&MyClass::start, std::ref(mine)); mine 的类型为 MyClass,这意味着您是按值传递的。 因此,std::thread 将其副本传递给新创建的线程。

您需要明确告诉模板您正在通过引用传递 mine


感谢您的及时回答。我明白传递引用是我一直想要的。但我不明白为什么在线程进入函数之前就调用了析构函数。如果它正在复制对象并在副本上工作,我不明白为什么它的析构函数会那么早被调用。您能否请澄清一下这个问题? - narengi
4
因为你从未记录复制/移动构造函数,所以你需要记录它们以了解实际发生的情况。 - NathanOliver
我知道析构函数应该被调用两次(尽管根据我的输出它被调用了3次?!)。我的问题是为什么"MyClass destructor is called"这一行在"MyClass is starting"这一行之前打印出来。 - narengi
@NathanOliver 我明白了,我会这样做的。非常好的建议,谢谢。 - narengi
1
@narengi std::thread 的构造函数可能会将其参数复制/移动到 std::thread 类内部的某种内部存储中。因此,您将拥有 3 个对象:minestd::thread::thread()(构造函数)的参数和 std::thread 中的数据成员。 - Angew is no longer proud of SO
显示剩余2条评论

3

std::thread内部创建本地对象并多次调用复制构造函数,一旦使用后销毁本地对象。因此,您会得到多个输出,如MyClass destructor is called。 如果要检查对象构建的行为,则可以在类中包含复制构造函数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接