[英]std::thread initialization with class argument results with class object being copied multiple times
It seems that if you create an object of a class, and pass it to the std::thread initialization constructor, then the class object is constructed and destroyed as much as 4 times overall. 似乎如果你创建一个类的对象,并将它传递给std :: thread初始化构造函数,那么类对象的构造和销毁总体上是4次。 My question is: could you explain, step by step, the output of this program?
我的问题是:你能一步一步地解释这个程序的输出吗? Why is the class being constructed, copy-constructed and destructed so many times in the process?
为什么这个类在这个过程中被构造,复制和破坏很多次?
sample program: 示例程序:
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <thread>
class sampleClass {
public:
int x = rand() % 100;
sampleClass() {std::cout << "constructor called, x=" << x << std::endl;}
sampleClass(const sampleClass &SC) {std::cout << "copy constructor called, x=" << x << std::endl;}
~sampleClass() {std::cout << "destructor called, x=" << x << std::endl;}
void add_to_x() {x += rand() % 3;}
};
void sampleThread(sampleClass SC) {
for (int i = 0; i < 1e8; ++i) { //give the thread something to do
SC.add_to_x();
}
std::cout << "thread finished, x=" << SC.x << std::endl;
}
int main(int argc, char *argv[]) {
srand (time(NULL));
sampleClass SC;
std::thread t1 (sampleThread, SC);
std::cout << "thread spawned" << std::endl;
t1.join();
std::cout << "thread joined" << std::endl;
return 0;
}
The output is: 输出是:
constructor called, x=92
copy constructor called, x=36
copy constructor called, x=61
destructor called, x=36
thread spawned
copy constructor called, x=62
thread finished, x=100009889
destructor called, x=100009889
destructor called, x=61
thread joined
destructor called, x=92
compiled with gcc 4.9.2, no optimization. 用gcc 4.9.2编译,没有优化。
There are a lot of copying/moving going on in the background. 在后台有很多复制/移动。 Note however, that neither the copy constructor nor the move constructor is called when the thread constructor is called.
但请注意,在调用线程构造函数时,既不调用复制构造函数也不调用移动构造函数。
Consider a function like this: 考虑这样的函数:
template<typename T> void foo(T&& arg);
When you have r-value references to template arguments C++ treats this a bit special. 当你对模板参数进行r值引用时,C ++对此有点特殊。 I will just outline the rules here.
我将在这里概述规则。 When you call
foo
with an argument, the argument type will be 当您使用参数调用
foo
时,参数类型将为
That is, either the argument will be passed as an r-value reference or a standard reference. 也就是说,参数将作为r值引用或标准引用传递。 Either way, no constructor will be invoked.
无论哪种方式,都不会调用构造函数。
Now look at the constructor of the thread object: 现在看一下线程对象的构造函数:
template <class Fn, class... Args>
explicit thread (Fn&& fn, Args&&... args);
This constructor applies the same syntax, so arguments will never be copied/moved into the constructor arguments. 此构造函数应用相同的语法,因此永远不会将参数复制/移动到构造函数参数中。
The below code contains an example. 以下代码包含一个示例。
#include <iostream>
#include <thread>
class Foo{
public:
int id;
Foo()
{
id = 1;
std::cout << "Default constructor, id = " << id << std::endl;
}
Foo(const Foo& f)
{
id = f.id + 1;
std::cout << "Copy constructor, id = " << id << std::endl;
}
Foo(Foo&& f)
{
id = f.id;
std::cout << "Move constructor, id = " << id << std::endl;
}
};
void doNothing(Foo f)
{
std::cout << "doNothing\n";
}
template<typename T>
void test(T&& arg)
{
}
int main()
{
Foo f; // Default constructor is called
test(f); // Note here that we see no prints from copy/move constructors
std::cout << "About to create thread object\n";
std::thread t{doNothing, f};
t.join();
return 0;
}
The output from this code is 这段代码的输出是
Default constructor, iCount = 1
About to create thread object
Copy constructor, id = 2
Move constructor, id = 2
Move constructor, id = 2
doNothing
int main(int argc, char *argv[]) {
sampleClass SC; // default constructor
std::thread t1 (sampleThread, SC); // Two copies inside thread constructor,
//use std::ref(SC) to avoit it
//..
}
void sampleThread(sampleClass SC) { // copy SC: pass by ref to avoid it
// but then modifications are for original and not the copy
// ...
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.