睡眠影响 std::thread 调用哪个虚拟成员函数?

Sleep affecting which virtual member function is called by std::thread?

本文关键字:虚拟成员 函数 thread 影响 std 调用      更新时间:2023-10-16

我不确定这是否是 c++11 中的预期行为。这是我发现的一个例子。

#include <iostream>
#include <thread>
using namespace std;
class A {
public:
virtual void a() = 0;
thread t;
A() : t(&A::a, this) {}
virtual ~A() {
t.join();
}
};
class B : public A {
public:
virtual void a() {
cout << "B::a" << endl;
}
};
int main() {
B b;
this_thread::sleep_for(chrono::seconds(1));
}

编译并运行时

$ g++ -std=c++11 -pthread test.cpp -o test
$ ./test
B::a
$

但是当睡眠被移除时...

int main() {
B b;
//this_thread::sleep_for(chrono::seconds(1));
}

奇怪的事情发生了

$ g++ -std=c++11 -pthread test.cpp -o test
$ ./test
pure virtual method called
terminate called without an active exception
Aborted (core dumped)
$

这可能是一个错误吗?

在构造函数和析构函数中时,virtual函数的行为不同。在A的构造函数中,B尚未初始化,这就是为什么B的虚函数还不可能被调用的原因。请参阅virtual

当从构造函数或析构函数直接或间接调用虚函数时(包括在构造或销毁类

的非静态数据成员期间,例如在成员初始值设定项列表中),并且调用适用的对象是正在构造或销毁的对象,则调用的函数是构造函数或析构函数类中的最终重写器,而不是在派生更多的类中重写它。换句话说,在构造或销毁期间,更多派生的类不存在。

因此,该成员函数指针在调用它时&A::a在其他线程中解析。此调用解析使用虚拟表,因为&A::a是指向虚拟成员函数的指针。构造函数做的第一件事是将虚拟表指针设置为类的虚拟表。如果在调用(this->&A::a)()时已输入B构造函数,则调用B::a。调用(this->&A::a)()的新线程与执行AB构造函数的当前线程之间存在争用条件。

一篇关于从构造函数启动线程的好文章

https://rafalcieslak.wordpress.com/2014/05/16/c11-stdthreads-managed-by-a-designated-class/