rudicangiotti
rudicangiotti

Reputation: 566

Questions about std::thread class

Considering the following class header:

class ThreadClass {
public:
  ThreadClass();
  ~ThreadClass();
  void operator()(int val);
  void set(int val);
  int get();
private:
  int x;
};

And the following implementation:

ThreadClass::ThreadClass(): x(0) {
  std::cout << "Constructing..." << std::endl;
}
ThreadClass::~ThreadClass() {
  std::cout << "Destructing..." << std::endl;
}
void ThreadClass::operator()(int val) {
  set(val);
  std::cout << get() << std::endl;
  set(val * 2);
  std::cout << get() << std::endl;
}
void ThreadClass::set(int val) {
  x = val;
}
int ThreadClass::get() {
  return x;
}

Launching the following main, class destructor is called several times instead of one:

int main(int argc, char const *argv[]) {
  std::thread x = std::thread(ThreadClass(), 10);
  x.join();
  return 0;
}

>>> Constructing...
>>> Destructing...
>>> Destructing...
>>> 10
>>> 20
>>> Destructing...

Since my knowledge about the std::thread class is not so deep, I would like to ask you:

  1. why this happens? Sincerely, I was expecting just a single instance of the ThreadClass.
  2. In order to avoid unexpected behaviour, is it necessary to overload some of the class operators?

Upvotes: 1

Views: 62

Answers (1)

Water
Water

Reputation: 3675

You are invoking implicit constructors that you haven't defined (yet have been made for you). You can see them in action here:

#include <thread>
#include <iostream>

class ThreadClass {
public:
  ThreadClass() {
    std::cout << "Constructing..." << std::endl;
  }
  
  ThreadClass(const ThreadClass& t) {
    std::cout << "Copy constructing..." << std::endl;
    this->x = t.x;
  }
  
//   ThreadClass(ThreadClass&& t) {
//     std::cout << "Move constructing..." << std::endl;
//     this->x = t.x;
//   }
  
  ~ThreadClass() {
    std::cout << "Destructing..." << std::endl;
  }
  
  void operator()(int val) {
      set(val);
      std::cout << get() << std::endl;
      set(val * 2);
      std::cout << get() << std::endl;
  }
  void set(int val) {
      x = val;
  }
  int get() {
      return x;
  }
private:
  int x;
};

int main(int argc, char const *argv[]) {
  std::thread x = std::thread(ThreadClass(), 10);
  x.join();
  return 0;
}

Output:

Constructing...

Copy constructing...

Copy constructing...

Destructing...

Destructing...

10

20

Destructing...

You can uncomment the move constructor if you want to see that in action as well.

To directly answer your questions:

why this happens? Sincerely, I was expecting just a single instance of the ThreadClass.

This happens because you are passing an object around, so the additional constructors mentioned in the post are getting called multiple times.

In order to avoid unexpected behaviour, is it necessary to overload some of the class operators?

What do you mean by unexpected behavior? This performs exactly how C++ intends. This is well defined behavior for C++.

Upvotes: 2

Related Questions