I'm trying to understand when the deconstructor of a functor (i.e. a class with () operator) is called when that functor is passed to a thread. But in the following example, the constructor is called for once but the deconstructor is called for three times?
According to
What are the missing (two) calls to the constructor? Is it copy or move?
If the move constructor is called, how can write the deconstructor so that it won't destroy the resources being moved? Imagine I open a file run() and close it in the ~run(), calling it three times causes a problem of course.
Example:
#include <iostream>
#include <thread>
using namespace std;
class run {
public:
run() { cout << "in run()" << endl; }
~run() { cout << "in ~run()" << endl; }
void operator()() {};
};
int main() {
run thread_r;
thread t(thread_r);
t.join();
}
Gives output:
in run()
in ~run()
in ~run()
in ~run()
Rule of 5, 3 and zero.
If you define a destructor, the compiler will still generate a default copy constructor and assignment operator.
Unfortunately, if you've defined a destructor it implies that you have some special handling around resource deallocation, so the default copy and assignment code will be plain wrong.
It is good practice, by which I mean "always do it, no excuses", to provide at least copy constructor and assignment operator, even if you delete them.
If you're going to supply these, you may as well go ahead and write correct move operators too.
#include <iostream>
#include <thread>
using namespace std;
class run {
public:
run() { cout << "in run()" << endl; }
run(const run&) { cout << "copied()" << endl; }
run(run&&) { cout << "moved()" << endl; }
run& operator=(const run&) { cout << "copy-assigned()" << endl; return *this; }
run& operator=(run &&) { cout << "move-assigned()" << endl; return *this; }
~run() { cout << "in ~run()" << endl; }
void operator()() {};
};
int main() {
run thread_r;
thread t(thread_r);
t.join();
}
example output:
in run()
copied()
moved()
in ~run()
in ~run()
in ~run()
Here's an updated version to help explain what's happening in the constructors and destructors:
#include <iostream>
#include <thread>
#include <vector>
using namespace std;
class run {
public:
run()
: lifetime("constructed")
{
cout << lifetime << endl;
}
run(const run& other)
: lifetime("copied from " + other.lifetime)
{
cout << lifetime << endl;
}
run(run&& other)
: lifetime("move-constructed from " + other.lifetime)
{
other.lifetime = "[zombie] - " + other.lifetime;
cout << lifetime << endl;
}
run& operator=(const run& other)
{
lifetime = "copy assigned from " + other.lifetime + ", was once " + lifetime;
cout << lifetime << endl;
return *this;
}
run& operator=(run &&other)
{
lifetime = "move-assigned from " + other.lifetime + ", was once " + lifetime;
other.lifetime = "[zombie] - " + other.lifetime;
cout << lifetime << endl;
return *this;
}
~run()
{
lifetime = "lifetime ending: " + lifetime;
cout << lifetime << endl;
}
void operator()() {};
std::string lifetime;
};
int main() {
run thread_r;
thread t(thread_r);
t.join();
}
sample output:
constructed
copied from constructed
move-constructed from copied from constructed
lifetime ending: [zombie] - copied from constructed
lifetime ending: move-constructed from copied from constructed
lifetime ending: constructed
For anyone not sure about the exact behaviour of copy constructors and move-constructors, it would be a good idea to play with this code in a debugger until it becomes clear.
Related
Some background
Today I saw, in the body of a function kaboom, a local shared_ptr pointing to a *global object and, being captured by reference and its pointee returned by reference by a lambda, i.e. [&local]() -> auto& { return *local; }; this lambda was stored somehow for further use after the function kaboom returned.
As soon as I saw this, I thought the code was invoking undefined behavior. I tried to make a minimal example to support my claim, and came up with the following.
#include <iostream>
#include <memory>
#include <functional>
#include <vector>
struct Resource {
const int i{3};
};
std::shared_ptr<Resource> global = std::make_unique<Resource>();
auto getSharedStuff() {
return global;
}
std::vector<std::function<Resource&()>> actions{};
void kaboom() {
std::shared_ptr<Resource> local = getSharedStuff();
actions.push_back([&local]() -> auto& { return *local; });
}
int main() {
kaboom();
std::cout << global->i << std::endl;
std::cout << actions[0]().i << std::endl;
}
The simple fact that the 2 couts give 3 and 0 respectively is proof for me that I'm observing UB (3 is correct, and 0 could have been anything, including 3, but I've been lucky that it was not 3, so the UB is well manifest).
Good.
The code I'm curious about
But before getting there, an intermediate version of the repro above was this:
#include <iostream>
#include <memory>
#include <functional>
#include <vector>
struct Resource {
Resource(Resource const&) = delete;
Resource(Resource&&) = delete;
Resource() { std::cout << this << "'s ctor" << std::endl; }
~Resource() { std::cout << this << "'s dtor" << std::endl; }
void operator()() { std::cout << this << "'s operator()" << std::endl; }
};
std::shared_ptr<Resource> global = std::make_unique<Resource>();
auto getSharedStuff() {
return global;
}
std::vector<std::function<Resource&()>> actions{};
void kaboom() {
std::shared_ptr<Resource> local = getSharedStuff();
actions.push_back([&local]() -> auto& { return *local; });
}
int main() {
kaboom();
std::cout << "---" << std::endl;
actions[0]()();
}
which can result in this output
0xc602b0's ctor
---
0x7f0f48f7f4a0's operator()
0xc602b0's dtor
I'm kind of ok with this, as actions[0]()() is dereferencing a destroyed shared_ptr and calling operator() on the screwed up result, so I accept that this can be screwup too.
What's funny, though, is that removing std::cout << "---" << std::endl; makes UB less manifest, as the output becomes something like this:
0xce92b0's ctor
0xce92b0's operator()
0xce92b0's dtor
So my question is: how can a "simple" line as std::cout << "---" << std::endl; affect this way the manifestation of UB?
I'm calling std::transform with a lambda that takes by reference and gives back a reference to the vector element. However, according to my program output, the copy constructor is called and the objects are NOT the same.
Code:
#include <algorithm>
#include <iostream>
#include <vector>
class Math
{
private:
int val_ = 5;
public:
Math(const Math& m) {
std::cout << "Copy constructor, our address: " << this << ", his address: " << &m << std::endl;
}
Math(int val) : val_(val) {
std::cout << "Object constructed with " << val << std::endl;
}
};
int main()
{
std::vector<Math> v_math = { { 5 }, { 10 } };
std::transform(
begin(v_math),
end(v_math),
begin(v_math),
[](const Math& m)-> const Math& {
return m;
});
}
Output (Godbolt):
Object constructed with 5
Object constructed with 10
Copy constructor, our address: 0x23d7ec0, his address: 0x7fff9dc499a8
Copy constructor, our address: 0x23d7ec4, his address: 0x7fff9dc499ac
So three things are unclear to me right now:
Why are the objects different? Shouldn't they be the same?
Why is one object's address bigger than the other? Is this because the copied-to object remains on the stack which has offset-pointers?
How can I avoid copy construction as well (actually I just "misuse" std::transform for a declarative way of invoking a lambda on every std::vector element)?
The copies have nothing to do with your usage of std::transform. They happen when you construct your v_math std::vector, because you're using a std::initializer_list constructor, which forces copies during construction.
In your std::transform call, operator=(const Math&) is called, change your code to the following to see this.
class Math
{
private:
int val_ = 5;
public:
Math(const Math& m) {
std::cout << "Copy constructor, our address: " << this << ", his address: " << &m << std::endl;
}
Math(int val) : val_(val) {
std::cout << "Object constructed with " << val << std::endl;
}
Math& operator=(const Math& other) {
val_ = other.val_;
std::cout << "Operator=(const Math&) called!\n";
return *this;
}
};
int main()
{
std::vector<Math> v_math = { { 5 }, { 10 } };
std::cout << "After constructing v_math!\n";
std::transform(
begin(v_math),
end(v_math),
begin(v_math),
[](const Math& m)-> const Math& {
return m;
});
std::cout << "After std::transform call!\n";
}
I have a block of code where I am using unique_ptr.
class Abc {
public:
std::string msg;
Abc(std::string m) {
msg = m;
std::cout << "Constructor: " << msg << std::endl;
}
~Abc() {
std::cout << "Destructor: " << msg << std::endl;
}
};
int main() {
auto p = std::make_unique<Abc>(Abc(__func__));
}
But the destructor is called two times. Is there a way I can make it call the destructor only one time?
You're constructing a temporary Abc (i.e. Abc(__func__)) firstly, then pass it to std::make_unique, which constructs the underlying Abc from the temporary (via the move constructor of Abc); i.e. two Abc objects are constructed, then destructor are called twice too.
You can pass __func__ to std::make_unique directly, i.e. needn't to construct the temporary Abc from the beginning.
auto p = std::make_unique<Abc>(__func__); // constructs Abc via Abc::Abc(std::string) directly
You create two objects and see both of them destructed. Abc(__func__) is a temporary that gets destroyed at the end of the line in main.
Add a user defined move constructor to see how make_unique move constructs the object:
#include <string>
#include <memory>
#include <iostream>
class Abc {
public:
std::string msg;
Abc(std::string m) {
msg = m;
std::cout << "Constructor: " << msg << std::endl;
}
Abc(Abc&& other){
msg = other.msg + " moved";
std::cout << "Move Constructor: " << msg << "\n";
}
~Abc() {
std::cout << "Destructor: " << msg << std::endl;
}
};
int main() {
auto p = std::make_unique<Abc>(Abc(__func__));
}
Output:
Constructor: main
Move Constructor: main moved
Destructor: main
Destructor: main moved
I have some troubles with an application that I made. std::move destroys your object when you move it to vector pushback. Here a small example:
#include <string>
#include <iostream>
#include <vector>
using namespace std;
class FileSetting
{
private:
FileSetting(FileSetting &fileSetting) { cout << "Copy\n"; }
public:
FileSetting(std::string name, void * value, int size) { cout << "Create\n"; }
FileSetting(FileSetting &&fileSetting) { cout << "Move\n"; }
~FileSetting() { cout << "Destroy\n"; }
void test() { cout << "Test\n"; }
};
int main()
{
vector<FileSetting> settings;
{
char * test = "test";
FileSetting setting("test", test, strlen(test) * sizeof(char));
settings.push_back(std::move(setting));
}
settings[0].test();
cout << "Done!\n";
return 0;
}
The output will be:
Create
Move
Destroy
Test
Done!
Destroy
How can I make sure that destroy only will be called when FileSetting goes out of scope and not when I move it. I'm trying to avoid pointer.
std::move() doesn't destroy the object. The "Destroy" you're getting is from setting going out of scope.
When I run the following program, the destructor is called twice and I'm trying to understand why?
#include <iostream>
#include <vector>
#include <algorithm>
class sample
{
public:
sample() { std::cout << "Constructor " << std::endl; }
~sample() { std::cout << "Destructor" << std::endl; }
void operator()(int i)
{
std::cout << i << " , " << std::endl;
}
};
int main()
{
std::vector<int> iNumbers;
for ( int i = 0 ; i < 5 ; ++i)
iNumbers.push_back(i);
std::for_each(iNumbers.begin() , iNumbers.end() , sample() );
}
The output is as folllows
Constructor
0 ,
1 ,
2 ,
3 ,
4 ,
Destructor
Destructor
Classic rule of three violation. Try this:
#include <iostream>
#include <vector>
#include <algorithm>
class sample
{
public:
sample() { std::cout << "Constructor " << std::endl; }
sample(const sample&) { std::cout << "Constructor (copy)" << std::endl; }
~sample() { std::cout << "Destructor" << std::endl; }
sample& operator=(const sample&) { return *this; }
void operator()(int i)
{
std::cout << i << " , " << std::endl;
}
};
int main()
{
std::vector<int> iNumbers;
for ( int i = 0 ; i < 5 ; ++i)
iNumbers.push_back(i);
std::for_each(iNumbers.begin() , iNumbers.end() , sample() );
}
Output is:
Constructor
0 ,
1 ,
2 ,
3 ,
4 ,
Constructor (copy)
Destructor
Destructor
The reason is that std::for_each takes its argument by value, and this results in a copy of the argument you provide being made.
Therefore, the destruction of the temporary you create by doing sample() will be responsible for one of those two destruction messages (the last one, since the temporary is destructed after the evaluation of the full expression that creates it).
The first destruction message, on the other hand, comes from the destruction of the copy that std::for_each is working on.
std::for_each will take the function object by value, causing it to be copied. So one destructor is called for the temporary object created by sample() and the other for the copy.
If you wrote a copy constructor, you would see that the functor is copied into the algorithm. Both copies are then destructed. There is a potential for the functor to be returned and there would be 3 copies, so one of the copies is being elided.