Reputation: 106
I have some class A
, which I can either construct using nothing, or an std::function
. On destruction that given function should be called (in case there is one). My problem is that the object gets destroyed right after it is created and returned by my getSomeA()
function which calls the std::function
before it is supposed to be called. The function passed to the constructor should only be called once. Some example code:
#include <iostream>
#include <functional>
static void testFunction(const std::string& msg)
{
std::cout << "TestFunction: " << msg << "\n";
}
class A {
public:
A(void) = default;
A(const std::function<void()>& onDestroy) :
onDestroy(onDestroy)
{ }
~A(void)
{
if (onDestroy) onDestroy();
else std::cout << "in dtor but no onDestroy was set\n";
}
private:
std::function<void()> onDestroy;
};
A getSomeA(void)
{
return A(std::bind(testFunction, "the A that was created inside getSomeA"));
}
int main(void)
{
A b1;
std::cout << "After creating b1\n";
b1 = getSomeA();
std::cout << "After reassigning b1\n";
std::cout << "Here the program works with b1...\n";
}
The program outputs
After creating b1
TestFunction: the A that was created inside getSomeA
After reassigning b1
Here the program works with b1...
TestFunction: the A that was created inside getSomeA
So the function is called before it is supposed to be called (at the end of int main()).
After adding a move constructor and assignment operator everything works as expected:
A(A&& other) :
onDestroy(std::exchange(other.onDestroy, nullptr))
{ }
A& operator=(A&& other)
{
onDestroy = std::exchange(other.onDestroy, nullptr);
return *this;
}
And the program outputs
After creating b1
in dtor but no onDestroy was set
After reassigning b1
Here the program works with b1...
TestFunction: the A that was created inside getSomeA
The actual question: Where is the object destroyed for the first time so that testFunction is called? In getSomeA()
or in the main function before the assignment but after the object was created in getSomeA()
?
All the edits: I tried to bring down my question for one hour but since I have no idea about move/copy semantics in C++ that was pretty hard for me.
Upvotes: 1
Views: 239
Reputation: 118292
In the first version the object gets destroyed for the first time as part of returning the object constructed in getSomeA
. Its return
statement effectively constructs a temporary object, which then gets assigned to main
's b1
. If you ignore the process of returning from a function, the sequence of events is:
A <temporary object>(std::bind( ... )
b1=<temporary object>
<temporary object gets destroyed>
At this point the bound function gets called, as a result of the temporary object gets destroyed. The temporary object is a fully tricked out object, with all the rights and privileged granted thereof. Including a destructor.
But wait, there's more! b1
is a perfect bit-by-bit copy of the original object, with the bound function, before it got destroyed. So when b1
gets destroyed, the function gets called again.
This is the indirect consequence of the Rule Of Three. When an object owns a resource, and must maintain an exclusive ownership of the resource you will need to provide a copy and/or move constructor and and assignment operator to spell out exactly what should happen in that situation.
P.S. these rules change slightly with C++17's guaranteed copy elision, but the underlying concept is still the same.
Upvotes: 1