我遇到了奇怪的 C++ 代码行为,不确定这是编译器错误还是我的代码的未定义/未指定行为。这是代码:
#include <unistd.h>
#include <iostream>
#include <thread>
struct Parent {
std::thread t;
static void entry(Parent* p) {
p->init();
p->fini();
}
virtual ~Parent() { t.join(); }
void start() { t = std::thread{entry, this}; }
virtual void init() { std::cout << "Parent::init()" << std::endl; }
virtual void fini() { std::cout << "Parent::fini()" << std::endl; }
};
struct Child : public Parent {
virtual void init() override { std::cout << "Child::init()" << std::endl; }
virtual void fini() override { std::cout << "Child::fini()" << std::endl; }
};
int main() {
Child c;
c.start();
sleep(1); // <========== here is it
return 0;
}
代码的输出如下,这并不奇怪:
Child::init()
Child::fini()
但是,如果函数调用“sleep(1)”被注释掉,输出将是:
Parent::init()
Parent::~fini()
在 Ubuntu 15.04 上测试,gcc-4.9.2 和 clang-3.6.0 表现出相同的行为。编译器选项:
g++/clang++ test.cpp -std=c++11 -pthread
它看起来像一个竞争条件(在线程启动之前 vtable 没有完全构建)。这段代码格式错误吗?编译器错误?或者它应该是这样的?
最佳答案
” The thread uses the child object, but the child object is destroyed before the thread is joined (because the joining only happens after the destruction of the child has begun).
Child
对象在 main
结束时被销毁。 Child
析构函数被执行,并有效地调用了 Parent
析构函数,其中 Parent
基(没有这样)和数据成员(线程对象)是被毁。随着析构函数在基类链中向上调用,对象的动态类型发生变化,其顺序与构造期间的变化顺序相反,因此此时对象的类型为Parent
.
线程函数中的虚拟调用可以发生在 Child
析构函数调用之前、重叠或之后,在重叠的情况下,有一个线程访问存储(实际上,vtable 指针) 正在被另一个线程更改。所以这是未定义的行为。
关于C++11 std::thread 和虚函数绑定(bind),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33172001/