Reputation: 905
The following C++ code is a simplification of a problem I am trying to solve.
It generates a random number then calls the functional-object to make a decision and generate output.
The code details a functional-object, a function and the main program. Sample output is provided. I've added copy constructor, copy assignment, move constructor & move assignment code to try and elucidate what is happening here.
I'm relatively new to using functional objects and have been trying to work out why there are so many calls to the copy constructor, move constructor and destructor. In fact excess calls to the destructor are causing trouble in my real application where functional-objects have allocated resources in their constructors.
My question is:
Is there a way modify this code so that a pointer or reference to the functional-object is passed around; so that there is no need to copy or move or destruct objects except when the program is finished?
#include <functional>
#include <iostream>
#include <stdlib.h>
#include <chrono>
#include <thread>
class RelativeValue
{
public:
//Constructor
RelativeValue(const int bdry) : boundary(bdry)
{
std::cout << "RelativeValue: Constructor" << std::endl;
}
//Copy Constructor
RelativeValue(const RelativeValue &orig) : boundary(orig.boundary)
{
std::cout << "RelativeValue: Copy Constructor" << std::endl;
}
//Copy assignment
RelativeValue& operator= (const RelativeValue &that)
{
std::cout << "RelativeValue: Copy Assignment Constructor" << std::endl;
if (this != &that)
{
boundary = that.boundary;
}
return *this;
}
//Move constructor
RelativeValue(RelativeValue &&orig) /*noexcept NOT VS2013*/ : boundary(orig.boundary)
{
std::cout << "RelativeValue: Move Constructor" << std::endl;
}
//Move Assignment
RelativeValue& operator=(RelativeValue &&that) /*noexcept NOT VS2013*/
{
std::cout << "RelativeValue: Move Assignment Constructor" << std::endl;
if (this != &that)
{
boundary = that.boundary;
}
return *this;
}
//Operators
bool operator==(const RelativeValue &anotherRelativeValue) const
{
return (boundary == anotherRelativeValue.boundary);
}
void operator()(const int &in)
{
if (in < boundary)
{
std::cout << in << " < " << boundary << std::endl;
}
else if (in > boundary)
{
std::cout << in << " > " << boundary << std::endl;
}
else if (in == boundary)
{
std::cout << in << " == " << boundary << std::endl;
}
}
~RelativeValue()
{
std::cout << "RelativeValue: Destructor Called." << std::endl;
}
private:
int boundary;
};
void reactor(const int &iterations, const std::function <void(int)> &functionObject)
{
int runLoop(0);
int number(0);
int returnValue(0);
srand(time(NULL));
while (runLoop < iterations)
{
number = rand() % 100 + 1;//in the range 1 to 100
functionObject(number);//call the functional-object
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
++runLoop;
}
}
int main(int argc, char* argv[])
{
//Want to create the functional-object here.
RelativeValue rv(50);
//Then pass the functional object to the reactor.
std::thread t1(reactor, 10, rv);//do the work.
t1.join();//wait for it.....
return 0;
}
Output:
RelativeValue: Constructor
RelativeValue: Copy Constructor
RelativeValue: Move Constructor
RelativeValue: Move Constructor
RelativeValue: Move Constructor
RelativeValue: Copy Constructor
RelativeValue: Destructor Called.
85 > 50
RelativeValue: Destructor Called.
RelativeValue: Destructor Called.
63 > 50
47 < 50
92 > 50
80 > 50
12 < 50
57 > 50
10 < 50
66 > 50
89 > 50
RelativeValue: Destructor Called.
RelativeValue: Destructor Called.
RelativeValue: Destructor Called.
Upvotes: 1
Views: 1014
Reputation: 14174
Functors are designed to act as powered-up plain old C functions, that means they should be small, or at least, be easy to copy. Functions are usually passed by value. Take the Standard Library <algorithm>
s as an example.
But even if you use operator()
for things that are not only a simple function, i.e. your functors carry a lot of state and machinery inside, you should not worry about copy ctors, destructors, etc. During release build with optimizations enabled most of that object bypassing around will be erased.
If you don't trust on compiler skills, the best solution is to use std::ref()
and std::cref()
, which create references with correct value semantics.
Upvotes: 2