线程中的 C++ 方法。传递的区别:对象,对象的地址,对象的std::ref
C++ method in thread. Difference between passing: object, object's address, std::ref of object
我正在尝试在 C++ 线程中执行对象的方法。
我可以通过将方法的地址和对象(或对象的地址,或 std::ref(my_obj))传递给线程的构造函数来做到这一点。
我观察到,如果我传递的是对象,而不是对象的地址或 std::ref(my_obj),那么对象会被复制 两次 (我正在复制构造函数中打印一些信息以查看)。
代码如下:
class Warrior{
string _name;
public:
// constructor
Warrior(string name): _name(name) {}
// copy constructor (prints every time the object is copied)
Warrior(const Warrior & other): _name("Copied " + other._name){
cout << "Copying warrior: \"" << other._name;
cout << "\" into : \"" << _name << "\"" << endl;
}
void attack(int damage){
cout << _name << " is attacking for " << damage << "!" << endl;
}
};
int main(){
Warrior conan("Conan");
// run conan.attack(5) in a separate thread
thread t(&Warrior::attack, conan, 5);
t.join(); // wait for thread to finish
}
我在这种情况下得到的输出是
Copying warrior: "Conan" into : "Copied Conan"
Copying warrior: "Copied Conan" into : "Copied Copied Conan"
Copied Copied Conan is attacking for 5!
而如果我只是将 &conan
或 std::ref(conan)
作为第二个参数传递给 thread t(...)
(而不是传递 conan
),则输出只是:
Conan is attacking for 5!
我有4个疑惑:
为什么我有 2 个对象副本而不是 1 个?
我期望通过将对象的实例传递给线程的构造函数,该对象会在线程自己的堆栈中被复制一次,然后attack()
方法将在该副本上调用。
线程的构造函数可以接受对象、地址或std::ref
的确切原因是什么?是不是用这个版本的构造函数(我承认我不是很懂)
template< class Function, class... Args >
explicit thread( Function&& f, Args&&... args );
在所有 3 个案例中?
如果我们排除第一种情况(因为它效率低下),我应该在&conan
和std::ref(conan)
之间使用什么?
这是否与 std::bind
所需的语法有某种关系?
Why is that I have 2 copies of the object instead of 1?
当你启动一个线程时,参数被复制到线程对象中。然后将这些参数复制到创建的实际线程中,因此您有两个副本。这就是为什么当你想传递函数通过引用获取的参数时必须使用 std::ref
的原因。
What is the exact reason why the thread's constructor can accept an object, an address, or a std::ref? Is it using this version of the constructor (which I admit I do not fully understand)
std::thread
基本上是通过
这样的调用来启动新线程
std::invoke(decay_copy(std::forward<Function>(f)),
decay_copy(std::forward<Args>(args))...);
std::invoke
是为处理所有不同类型的可调用对象而构建的,其中之一是当它有一个成员函数指针和一个对象时,它会适当地调用函数。它还知道 std::reference_wrapper
并且可以处理调用指向对象 std::reference_wrapper
上的成员函数的指针。
If we exclude the first case (since it's inefficient), what should I use between &conan
and std::ref(conan)
?
这主要是基于意见。他们基本上都做同样的事情,尽管第一个版本写起来更短。
Is this somehow related to the syntax required by std::bind
?
有点。 std::bind
的 operator()
也是使用 std::invoke
实现的,所以它们有一个非常通用的接口。
所有这些都说明了您可以使用 lambda 为自己提供一个通用接口。
thread t(&Warrior::attack, conan, 5);
可以改写为
thread t([&](){ return conan.attack(5); });
并且您可以将此表单用于几乎所有您想要调用的其他函数。我发现看到 lambda 时更容易解析。
我正在尝试在 C++ 线程中执行对象的方法。
我可以通过将方法的地址和对象(或对象的地址,或 std::ref(my_obj))传递给线程的构造函数来做到这一点。
我观察到,如果我传递的是对象,而不是对象的地址或 std::ref(my_obj),那么对象会被复制 两次 (我正在复制构造函数中打印一些信息以查看)。
代码如下:
class Warrior{
string _name;
public:
// constructor
Warrior(string name): _name(name) {}
// copy constructor (prints every time the object is copied)
Warrior(const Warrior & other): _name("Copied " + other._name){
cout << "Copying warrior: \"" << other._name;
cout << "\" into : \"" << _name << "\"" << endl;
}
void attack(int damage){
cout << _name << " is attacking for " << damage << "!" << endl;
}
};
int main(){
Warrior conan("Conan");
// run conan.attack(5) in a separate thread
thread t(&Warrior::attack, conan, 5);
t.join(); // wait for thread to finish
}
我在这种情况下得到的输出是
Copying warrior: "Conan" into : "Copied Conan"
Copying warrior: "Copied Conan" into : "Copied Copied Conan"
Copied Copied Conan is attacking for 5!
而如果我只是将 &conan
或 std::ref(conan)
作为第二个参数传递给 thread t(...)
(而不是传递 conan
),则输出只是:
Conan is attacking for 5!
我有4个疑惑:
为什么我有 2 个对象副本而不是 1 个?
我期望通过将对象的实例传递给线程的构造函数,该对象会在线程自己的堆栈中被复制一次,然后
attack()
方法将在该副本上调用。线程的构造函数可以接受对象、地址或
std::ref
的确切原因是什么?是不是用这个版本的构造函数(我承认我不是很懂)template< class Function, class... Args > explicit thread( Function&& f, Args&&... args );
在所有 3 个案例中?
如果我们排除第一种情况(因为它效率低下),我应该在
&conan
和std::ref(conan)
之间使用什么?这是否与
std::bind
所需的语法有某种关系?
Why is that I have 2 copies of the object instead of 1?
当你启动一个线程时,参数被复制到线程对象中。然后将这些参数复制到创建的实际线程中,因此您有两个副本。这就是为什么当你想传递函数通过引用获取的参数时必须使用 std::ref
的原因。
What is the exact reason why the thread's constructor can accept an object, an address, or a std::ref? Is it using this version of the constructor (which I admit I do not fully understand)
std::thread
基本上是通过
std::invoke(decay_copy(std::forward<Function>(f)),
decay_copy(std::forward<Args>(args))...);
std::invoke
是为处理所有不同类型的可调用对象而构建的,其中之一是当它有一个成员函数指针和一个对象时,它会适当地调用函数。它还知道 std::reference_wrapper
并且可以处理调用指向对象 std::reference_wrapper
上的成员函数的指针。
If we exclude the first case (since it's inefficient), what should I use between
&conan
andstd::ref(conan)
?
这主要是基于意见。他们基本上都做同样的事情,尽管第一个版本写起来更短。
Is this somehow related to the syntax required by
std::bind
?
有点。 std::bind
的 operator()
也是使用 std::invoke
实现的,所以它们有一个非常通用的接口。
所有这些都说明了您可以使用 lambda 为自己提供一个通用接口。
thread t(&Warrior::attack, conan, 5);
可以改写为
thread t([&](){ return conan.attack(5); });
并且您可以将此表单用于几乎所有您想要调用的其他函数。我发现看到 lambda 时更容易解析。