I'm studying to understand class constructor and destructor.
I have written a small console code to add one class instance in to a vector. All is nice and dandy, but what I fail to understand is, that adding one Object in to the vector triggers destructor twice. Why does this happen?
If I don't add any object, the vector doesn't trigger constructor or destructor on its own, so why it happens twice?
Can anyone explain why this happens?
#include <cstdio>
#include <vector>
class Test
{
private:
int value;
public:
Test()
{
printf("\nClass constructor triggered.");
};
~Test()
{
printf("\nClass desctructor triggered.");
}
};
int main()
{
std::vector<Test> container;
container.push_back( Test() );
return 0;
}
UPDATE:
I added some more information to the class so that I get more specific output, however now I noticed that with each addition to the vector the move-construction and destructor calls increase. Are the amount of these calls tied to the amount of objects within the vector or what is happening?
Am I having a leak? Sorry if too stupid questions.
Below is the added code:
#include <cstdio>
#include <vector>
class Test
{
private:
int value;
public:
// Constructor
Test(int v=0)
{
value = v;
printf("\n\n%i", value);
printf("\nClass constructor triggered.");
};
// Copy-move constructor
Test(Test&&)
{
printf("\nClass move-constructor triggered.");
};
// Destructor
~Test()
{
value = 0;
printf("\nClass desctructor triggered.");
}
};
int main()
{
std::vector<Test> container;
container.push_back( Test(1) );
container.push_back( Test(2) );
container.push_back( Test(3) );
container.push_back( Test(4) );
printf("\n\nPushback complete!");
return 0;
}
Your vector contains a copy of the object you add to it through push_back(). The first destructor invocation is caused by the temporary you create being destroyed at the end of the full expression containing the call to push_back(). The second destructor is caused by the copy inside the vector being destroyed when the vector itself is destroyed.
You can convince yourself by adding a diagnostic to main():
int main()
{
std::vector<Test> container;
container.push_back( Test() );
printf("\nThis is before the vector is destroyed...");
return 0;
}
You can observe the output in this live example.
The copy which your vector contains is created by invoking the automatically-generated move constructor for your class (rather than using default construction), which is why you don't see a corresponding construction diagnostic.
If you defined your own move constructor (or copy constructor, as shown below) to emit a diagnostic, the output would be closer to what you'd expect:
Test(Test const&)
{
printf("\nCopy construction triggered.");
};
Again, live example.
Because you don't print every constructor invocation, you're missing out on move-constructor call. Your class, apart from the default constructor you've provided, has also implicitly generated move and copy constructors.
The vector stores a value, and that value has to be initialized in some way. Typically, this happens either via a move c-tor or copy c-tor, altough an object might also be created directly inside of the vector using e.g. emplace_back.
Try adding this:
Test(Test&&)
{
printf("\nClass move constructor triggered.");
};
to your class, it should change the output to something that makes more sense (I've also added a print at the end of main):
Live On Coliru
Class constructor triggered.
Class moveconstructor triggered.
Class desctructor triggered.
Out of main scope.
Class desctructor triggered.
The first destructor call destroys moved-out "empty" instance of your class, while the second one fires when the vector itself is destroyed.
The push_back() does not trigger any destructor (in this case).
The two calls to Test's destructor are:
1 - Because you pass a temporary to push_back(), so that object get destroyed when push_back() is done
2 - when the program end, so the vector get destroyed and so it is it's content
For the sake of simplicity, let's assume you are working with C++03, and move semantics are not available yet.
Add copy constructor to see that it is also triggered
Test(const Test&)
{
printf("\nClass copy constructor triggered.");
};
Output
Class constructor triggered.
Class copy constructor triggered.
Class destructor triggered.
Class destructor triggered.
So, there are two objects constructed/destructed.
Roughly speaking, your code is equal to
int main()
{
std::vector<Test> container;
Test test; // first object created
container.push_back(test); // second object created by copying
return 0;
}
Related
Have looked at various similar questions here but still can't figure out why the following code does not compile:
// these three are defined somewhere
class A;
std::unique_ptr<A> make_a();
void take_a(std::unique_ptr<A>&&);
int main(){
take_a(make_a()); // this fails
return 0;
}
According to this:
If the default deleter is used, T must be complete at the point in
code where the deleter is invoked, which happens in the destructor,
move assignment operator, and reset member function of
std::unique_ptr.
As far as I understand, none of these (destructor, move assignment operator, nor reset member function) happens in main.
So why does compiler needs the definition of A here?
Since main has a unique_ptr within its scope, realistically it would need to know how to delete the object it holds.
It's possible that take_a doesn't actually take ownership of the object, thus main would need to delete.
main gets a temporary unique_ptr from make_a(), let's call it X. It then passes an rvalue reference to X to take_a. It still has the destroy X. Hence, it has to call the destructor after take_a, even though X would typically be empty at that point.
We need class A description (at least constructor and destructor) in order to create and move std::unique_ptr<A> objects; take_a(make_a()) is creating such object because make_a() is pass-by-value. It first creates a new unique_ptr<A> object, initialize it (using default constructor), and then update its value and returns this new object. Here the default constructor/destructor is being used, which the compiler is unable to find.
#include <bits/stdc++.h>
class A {
public: A() {}
public: ~A() {}
};
std::unique_ptr<A> make_a() {
std::cout << "make";
std::unique_ptr <A> tt = nullptr;
return tt;
}
void take_a(std::unique_ptr<A>&&) {
std::cout << "take";
}
int main(){
take_a(make_a()); // this works now
return 0;
}
Edit: Forgot to add the link. Works the other way too.
#include <iostream>
#include<vector>
using namespace std;
class test{
public:
test(){
cout<<"constructor called"<<endl;
}
test(const test& obj){
cout<<"copy constructor called"<<endl;
}
test(test&& obj){
cout<<"Move constructor called"<<endl;
}
};
int main()
{
vector<test> vec;
vec.emplace_back(test());
return 0;
}
When i run above program I expected emplace_back to create object in vector in place.Thus "constructor called" should have been output since emplace_back would avoid creating temporary object.
But the output is:
constructor called
Move constructor called
Here, temporary object is created just like push_back. Please explain.
emplace_back doesn't construct temporaries, but you constructed a temporary object explicitly by test(), then the new element is added to the vector from the temporary by the move constructor.
You can just
vec.emplace_back();
With emplace_back(test()) you already created an object outside of emplace_back and it has a move constructor so it is move-constructed. So you should call it without any argument for this case. Then you will not see any copy/move constructor calls.
vec.emplace_back(); // Will create a test object with constructor `test()` internally
To further understand, if your test class have more constructors, you can give emplace_back with those constructors. For example,
class test {
...
test(int a, int b);
test(const char* c);
};
And you can do this.
vec.emplace_back(1, 2);
vec.emplace_back("abcd");
This does not create redundant object which is cannot be done with push_back.
With emplace_back, the method already knows what class type that you're adding to your vector (you name it when initialising the vector), so the input arguments for emplace_back is only the arguments for the constructor that you want to call (typically you want to avoid the copy constructor, whose argument is an object of the same class):
struct A
{
A (int a, int b, int c)
{
// do something
}
A (const A & other)
{
//do something else
}
};
std::vector<A> array;
array . emplace_back (1, 2, 3);
// the above finds the constructor with these input arguments
// makes the new object within the vector - no copy
A obj (4, 5, 6);
array . emplace_back ( obj );
// the above looks for the constructor with this object (A)
// it finds a constructor (the copy constructor) and copies
array . emplace_back ( A (1,2,3) );
// the above first processes the inner part: making a new A object
// then searches for a constructor with that argument (an object A)
// in this case that's the copy constructor
In your case you were wanting to call a constructor with no arguments. This is why you want to use emplace_back() with no arguments to use this vector method correctly.
I got the follwing block of code:
#include <vector>
#include <iostream>
struct TestStruct {
bool wasCreated;
TestStruct() {
std::cout << "Default Constructor" << std::endl;
wasCreated = false;
}
~TestStruct() {
if (wasCreated) {
DoImportantStuff();
}
}
void Create() {
wasCreated = true;
}
// delete all copy stuff
TestStruct(const TestStruct&) = delete;
TestStruct& operator=(const TestStruct&) = delete;
// implement only move assignment & ctor
TestStruct(TestStruct&& other) {
std::swap(wasCreated, other.wasCreated);
}
TestStruct& operator=(TestStruct&& other) {
std::swap(wasCreated, other.wasCreated);
return *this;
}
// very important stuff
void DoImportantStuff() {
std::cout << "Do Important Stuff" << std::endl;
}
};
int main() {
std::vector<TestStruct> testVector;
testVector.emplace_back(TestStruct());
testVector.emplace_back(TestStruct());
std::cin.get();
}
This code leads to the output:
Default Constructor
Do Important Stuff
Default Constructor
Do Important Stuff
Do Important Stuff
Basicly I wanted to write a class, which owns memory but allocates this memory only when I call Create(). To avoid memory leaks and to avoid deleting not allocated memory I introduced wasCreated which will be only true when i call Create(). Every TestStruct should be saved in one vector. So in implemented move assigment & ctor and deleted both copy assigment & ctor.
Now it seems to me that the vector doenst call interally the default constructor of my TestStruct when its allocates new memory. Why is so and how do get the vector to call the default constructor on memory allocation? Do I need my own allocator?
Your problem is that your move constructor is implemented incorrectly. It swaps wasCreated between the newly created object and the one being moved from, but the variable in the newly created object has not been initialized yet (a default-constructed bool has an unknown value). So your temporary objects created with TestStruct() receive an uninitialized bool, which happens to be true in your case, hence the calls to DoImportantStuff() in their destructors.
So the move constructor should look something like this:
// implement only move assignment & ctor
TestStruct(TestStruct&& other) : wasCreated(other.wasCreated) {
other.wasCreated = false;
}
(You have moved ownership to the newly created object, the old one doesn't own anything anymore.)
Don't confuse the assignment operator with the constructor; they do different things. The assignment operator deals with two objects that are both already constructed; in the case of the constructor, the object being constructed is, well..., not constructed yet, so it doesn't have a valid state.
By the way, emplace_back() is pointless the way you're using it. Its purpose is to forward its arguments directly to the constructor of the object inside the vector. Since you have a default constructor (no arguments), the call should be:
testVector.emplace_back();
This will default-construct the TestStruct in place.
Now it seems to me that the vector doenst call interally the default constructor of my TestStruct when its allocates new memory.
A default-constructed vector has zero size, so there are no objects to construct.
If you want the vector to default-construct some objects, resize it.
Consider the below program:
class A
{
public:
A(int i)
{
cout<<"Called"<<endl;
}
};
int main()
{
vector<A> v(5,A(1));
return 0;
}
I am getting the output: http://ideone.com/81XO6
Called
Why the constructor gets called only once even if we are constructing 5 objects?
How vector is internally handled by the compiler?
Your class has two constructors and you are watching only one of them. std::vector creates its elements by copy-constructing them from the original element you supplied. For that purpose, the copy-constructor of class A is called 5 times in your example.
The copy-constructor for A in your example is implicitly declared and defined by the compiler. If you so desire, you can declare and define it yourself. If you print something from it, you will see that it is called at least 5 times.
It gets called once since the line
vector<A> v(5,A(1));
will call the constructor and the line becomes
vector v(5,X);
where X is the object constructed after calling the constructor.
After that the copy constructor is used.
Try adding
A(const &A);
To the class declaration to verify this.
myClassVar = MyClass(3);
I expected destructor being called on the previously created myClassVar on the left.
But it is actually being called on the new object that's created by MyClass(3).
My full test code and output follows..
edit
How do I fix the problem?
Implement an assignment operator?
MyClass actually has pointers, and MYSQL_STMT*, I wonder how should I deal with MYSQL_STMT* variable.
I just need MyClassVar(3) object not the MyClassVar() which was first created when ClientClass object was created.
I came across this situation fairly often, and wonder if there's a good way to do it.
#include <stdio.h>
class MyClass
{
public:
MyClass() { printf("MyClass %p\n", this); }
MyClass(int a) { printf("Myclass(int) %p\n", this); }
~MyClass() { printf("~MyClass %p\n", this); }
private:
int mA;
};
class ClientClass
{
public:
void Foo()
{
printf("before &myClassVar : %p\n", &myClassVar);
myClassVar = MyClass(3); // this is the important line
printf("after &myClassVar : %p\n", &myClassVar);
}
private:
MyClass myClassVar;
};
int main()
{
ClientClass c;
c.Foo();
return 0;
}
MyClass 0x7fff5fbfeba0
before &myClassVar : 0x7fff5fbfeba0
Myclass(int) 0x7fff5fbfeb70
~MyClass 0x7fff5fbfeb70 // <--- here destructor is called on the newly created object
after &myClassVar : 0x7fff5fbfeba0
~MyClass 0x7fff5fbfeba0
Here's how the critical line breaks down:
myClassVar = MyClass(3);
First, MyClass(3) calls constructor and returns the object.
Second, myClassVar = copies the object to myClassVar.
Then the statement ends. The object (which is an immediate) is dead, and thus the destructor is invoked.
EDIT :
As for how to get around this. The only way I can think of is to use a placement new. I'm not sure if there's a better solution other than making a "set" method.
myClassVar = MyClass(3);
myClassVar continues to exist after this line. The lifetime of MyClass(3) ends at the semicolon.
As the other posts mentioned the object with the custom constructor MyClass(3) gets destroyed after the assignment operation myClassVar = MyClass(3). In this case you do not need a custom assignment operator because the compiler generated one copies the member mA to the already existing object myClassVar.
However since MyClass defines its own destructor you should adhere to the rule of three, which mandates that in such a case you should implement a custom assignment operator as well.
Responding to your edit: how do you fix what problem? It's not clear
what the problem is. If your class needs a destructor (and there's no
polymorphism in play), it probably needs both an assignment operator and
a copy constructor. Similarly, when "tracking" construcctions and
destructions, you should probably provide both as well, since they will
be called.
Otherwise: if the problem is that you're constructing and then
assigning, rather than constructing with the correct value immediately,
the simple answer is "don't do it". The compiler does what you tell it
to. If you write:
MyClass var;
var = MyClass(3);
you have default construction, followed by the construction of a
temporary, assignment, and the destruction of the temporary. If you
write:
MyClass var(3);
or
MyClass var = 3;
you only have one construction. (Note that despite appearances, there
is no assignment in the last snippet. Only construction.)
For class members, this difference appears in the way you write the
constructor:
ClientClass::ClientClass() { var = MyClass(3); }
is default construction, followed by creation, assignment and
destruction of a temporary;
ClientClass::ClientClass() : var( 3 ) {}
is just construction with the correct value. (Rather obviously, this
second form is preferred.)