Std :: initialize the stream with the results of the class argument with multiple copying of the class object

It seems that if you create a class object and pass it to the std :: thread initialization constructor, then the class object will be built and destroyed as much as 4 times as a whole. My question is: could you explain step by step the conclusion of this program? Why is a class created, copied, and destroyed so many times in a process?

Program Example:

#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;
}

Output:

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, without optimization.

+4
source share
2 answers

/. , , , .

​​:

template<typename T> void foo(T&& arg);

r-value , ++ . . foo ,

  • && - r-
  • & -

, r-, . .

:

template <class Fn, class... Args>
explicit thread (Fn&& fn, Args&&... args);

, / .

.

#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;
}

Default constructor, iCount = 1
About to create thread object
Copy constructor, id = 2
Move constructor, id = 2
Move constructor, id = 2
doNothing
  • .
  • , , , .
  • l- , l-value reference, ( ) .
  • ( )
  • , , doNothing thread-function
+1
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
  // ...
}

​​

0

Source: https://habr.com/ru/post/1619736/


All Articles