10

我正在阅读 C++ Concurrency in Action 一书,以了解有关线程和 C++ 内存模块的更多信息。我很好奇复制构造函数在以下代码中被调用的次数:

struct func
{
    func() = default;
    func(const func& _f) {}

    void operator()() {}
};

int main()
{
    func f;
    std::thread t{ f };
    t.join();

    return 0;
}

当我在 Visual Studio 2013 调试器中浏览这段代码时,我看到复制构造函数被调用了四次。从主线程调用它三次,然后从新线程调用一次。我期待一个,因为它为新线程制作了对象的副本。为什么要创建三个额外的副本?

4

1 回答 1

1

如果您在复制构造函数中设置断点,您可以在调用堆栈窗口中看到构造函数调用上下文。在调试模式下,我在调用构造函数时发现了下一点:

  • 首先将功能对象复制到辅助函数中bind

  • 然后将功能对象移动到内部功能对象中_Bind

  • 之后_LaunchPad创建一个用于启动线程的类。在
    构造函数中,它需要 _Bind 实例的右值引用,所以我们有
    另一个移动构造函数调用

  • _LaunchPad在新线程中创建它的副本时调用move 构造函数。

因此,在您的情况下,我们有 4 个复制构造函数调用。如果您添加了移动构造函数,您将看到 1 个复制构造函数和 3 个移动构造函数调用。

在发布模式下,所有空的构造函数调用都被省略,汇编代码看起来很简单

于 2014-01-02T19:27:43.917 回答