I want to learn what differences are between push_back() and emplace_back(), especially when the elements are class type.So I write code like following:
int cnt = 0;
class foo{
public:
//constructor
foo() : ucnt(++cnt) { cout << ucnt << "\tdefault" << endl; }
foo(const foo&) : ucnt(++cnt) { cout << ucnt << "\tcopy" << endl; }
~foo() { cout << ucnt << "\tdestroyed" << endl; }
int ucnt;
};
int main()
{
vector<foo> vf = {foo()};
cout << vf.size() << " : " << vf[0].ucnt << endl;
vf.push_back(foo());
cout << vf.size() << " : " << vf[0].ucnt << " " << vf[1].ucnt << endl;
vf.emplace_back(foo());
cout << vf.size() << " : " << vf[0].ucnt << " " << vf[1].ucnt << " " << vf[2].ucnt << endl;
return 0;
}
whose result is:
1 default
2 copy
1 destroyed
1 : 2
3 default
4 copy
5 copy
2 destroyed
3 destroyed
2 : 5 4
6 default
7 copy
8 copy
9 copy
5 destroyed
4 destroyed
6 destroyed
3 : 8 9 7
8 destroyed
9 destroyed
7 destroyed
It seems like all the elements in vf are copyed and then destroyed while executing push_back() and emplace_back().Why?
emplace_back
The advantage of emplace_back is that it passes its arguments directly to the constructor of the to-be-emplaced class and constructs the new object in place instead of copy constructing it.
e.g.
elections.emplace_back("Nelson Mandela", "South Africa", 1994);
As opposed to push_back, where you pass a temporary object and copy construct the new object.
e.g.
elections.push_back(President("Franklin Delano Roosevelt", "the USA", 1936));
so in your case it should be
vf.emplace_back();
since you have no ctor arguments to be passed to the in place constructor. Compared to
vf.push_back(foo());
Constructor balance
The many unexpected copies and deletes stem from resizing the vector.
up to 1. cout
1 temporary created, and destroyed in constructor
= 1x default, 1x copy, 1x destroyed ✔️
up to 2. cout
1 temporary created and destroyed in push_back
1 existing object copied to the resized vector, therefor copy-created and destroyed
= 1x default, 2x copy, 2x destroyed ✔️
up to 3. cout
1 temporary created and destroyed in emplace_back
2 existing objects copied to the resized vector, therefor copy-created and destroyed
= 1x default, 3x copy, 3x destroyed ✔️
and so on
Edit: Example
The following code is from Artemy Vysotsky, see comment under this answer and shows exactly to the point how it is done right. Especially note the use of .reserve(3) to avoid reallocation orgies.
#include <vector>
#include <iostream>
using std::cout;
int cnt = 0;
class foo{
public:
//constructor
foo() : ucnt(++cnt) { cout << ucnt << "\tdefault\n" ; }
foo(const foo&) : ucnt(++cnt) { cout << ucnt << "\tcopy\n" ; }
foo(foo&&) noexcept : ucnt(++cnt) { cout << ucnt << "\tmove\n" ; }
~foo() { cout << ucnt << "\tdestroyed\n" ; }
int ucnt;
};
int main()
{
std::vector<foo> vf = {foo()};
cout << vf.size() << " 1: " << vf[0].ucnt << '\n';
vf.reserve(3);
cout << vf.size() << " 2: " << vf[0].ucnt << '\n';
vf.push_back(foo());
cout << vf.size() << " 3: " << vf[0].ucnt << " " << vf[1].ucnt << '\n';
vf.emplace_back();
cout << vf.size() << " 4: " << vf[0].ucnt << " " << vf[1].ucnt << " " << vf[2].ucnt << '\n';
return 0;
}
/***************
Output
$ ./test
1 default
2 copy
1 destroyed
1 1: 2
3 move
2 destroyed
1 2: 3
4 default
5 move
4 destroyed
2 3: 3 5
6 default
3 4: 3 5 6
3 destroyed
5 destroyed
6 destroyed
*****************/
Related
I have written a simple program to test "vector.erase" feature. There is a simple class (MyClass0) which writes some related message in it's constructor and another in it's destructor. And then there is a vector which contains 4 objects of type MyClass0. As I erase the second element of the vector:
vec0.erase(vec0.begin() + 1);
I suppose that the Message "GoodBye From 2" should be outputted on the screen. But the message "GoodBye From 4" is shown. It seems that the destructor of the 4'th element of the vector is called. (Although it is not the case, because the 4'th element will be destructed at the end, when the "main" is finished).
can anyone help me please so that I can find out the reason. The code and the output which is shown on the screen are:
Code:
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
class MyClass0
{
public:
MyClass0(int i_i_) : i_(i_i_)
{
cout << "Hello From " << this->i_ << endl;
}
~MyClass0()
{
cout << "GoodBye From " << this->i_ << endl;
}
std::string MyToString()
{
return std::string("This is ") + std::to_string(this->i_);
}
private:
int i_;
};
int main()
{
std::vector<MyClass0> vec0 = { MyClass0(1), MyClass0(2), MyClass0(3), MyClass0(4) };
cout << endl << "Before erasing..." << endl;
vec0.erase(vec0.begin() + 1);
cout << "After erase" << endl << endl;
return 0;
}
Output on the screen:
Hello From 1
Hello From 2
Hello From 3
Hello From 4
GoodBye From 4
GoodBye From 3
GoodBye From 2
GoodBye From 1
Before erasing...
GoodBye From 4
After erase
GoodBye From 1
GoodBye From 3
GoodBye From 4
https://godbolt.org/z/qvrcb81Ma
Her is your code modified a bit
class MyClass0
{
public:
MyClass0(int i_i_) : i_(i_i_)
{
cout << "Hello From " << this->i_ << endl;
}
~MyClass0()
{
cout << "GoodBye From " << this->i_ << endl;
}
std::string MyToString()
{
return std::string("This is ") + std::to_string(this->i_);
}
MyClass0(const MyClass0& other) : i_{other.i_}
{
std::cout << "Copy construct " << i_ << '\n';
}
MyClass0& operator=(const MyClass0& other)
{
std::cout << "Asign " << other.i_ << " onto " << i_ << '\n';
i_ = other.i_;
return *this;
}
private:
int i_;
};
What exposes what actually happens:
https://godbolt.org/z/hW177M7o6
When vector removes item from a middle it assigns items using operator= shifting them to the left and then deletes last item.
A vector is not allowed to have any holes in the middle. That means when you erase the second element you don't actually remove it. What happens is all of the elements get moved forward to fill in the hole, and after that the last element in the vector can be removed as it has been moved forward once
//start with
1 2 3 4
// erase 2, so move 3 into 2 and 4 into 3
1 3 4 *
// * is old 4 and we don't need that so remove it from the collection
1 3 4
// removing * calls the destructor for that element
Working with a union of two classes, it appears in this simple example that the union remembers the last class stored in it and calls the correct destructor for that object:
#include <iostream>
using std::cout;
using std::endl;
struct yes{
yes(){cout<<"yes-c"<<endl;}
~yes(){cout<<"yes-d"<<endl;}
};
struct no{
no(){cout<<"no-c"<<endl;}
~no(){cout<<"no-d"<<endl;}
};
struct u{
union{
yes y;
no n;
};
u(yes _y):y(_y){}
u(no _n):n(_n){}
~u(){}
};
int main() {
yes y;
no n;
{
u uu(n);
}
return 0;
}
Output:
yes-c
no-c
no-d
no-d
yes-d
So the uu will call the correct destructor ~no() for the union, as if it records the type when the union is constructed. How does this work?
Short answer: It doesn't.
If you add a copy-constructor to no you will see that there are actually three no objects being created, but only two are destructed.
First you create the object n. Then when you pass it by value to the u constructor, it is copied once into the _n argument. That _n object is then copied into the uu.n member.
The destructions are of the _n argument in the u constructor, and the n object in the main function.
Here's your program with some slight modification to add the copy-constructor and to keep track of the no objects:
#include <iostream>
struct yes{
yes(){std::cout<<"yes-c"<<std::endl;}
~yes(){std::cout<<"yes-d"<<std::endl;}
};
struct no{
no(){std::cout<<"no-c : "<<n<<std::endl;}
no(no const& o)
: n(o.n + 1)
{
std::cout << "no-cc : " << o.n << " -> " << n << '\n';
}
~no(){std::cout<<"no-d : "<<n<<std::endl;}
int n = 0;
};
struct u{
union{
yes y;
no n;
};
u(yes _y):y(_y){}
u(no _n):n(_n){}
~u(){}
};
int main()
{
yes y;
no n;
{
u uu(n);
}
}
Without optimizations or copy-elision this will create the output
yes-c
no-c : 0
no-cc : 0 -> 1
no-cc : 1 -> 2
no-d : 1
no-d : 0
yes-d
The output no-c : 0 is for the creation of the n object in the main function.
The output no-cc : 0 -> 1 is for the copying into the u constructor argument _n.
The output no-cc : 1 -> 2 is for the copying of the argument _n into the unions n object.
The output no-d : 1 is the destruction of the _n argument.
The output no-d : 0 is the destruction of the n object in the main function.
unions never "remember" which field is active.
Your code outputs:
yes-c
no-c
no-d
no-d
yes-d
You seem to interpret the 3rd line (no-d) as a destructor call for a union field of your class, but that's not what happens here. ~u() isn't going to call destructors for y nor n.
You pass parameters to your constructors by value, thus this line is a destructor call for the parameter of the constructor.
No, a union doesn't "know" it's active member. We have std::variant for that job. What you see is the result of the temporary created in the constructor for your union.
When making a union of non-trivial types. You are very likely to fall into traps of invoking Undefined Behavior as your program gets more complex; std::variant does a good job tagging your union.. And it will manage lifetimes appropriately
A union will be large enough to hold the largest variable declared within the union so that an object of any of the variable types declared in the union can be stored there.
However what is stored within the union is not tracked. Any object stored in a union must be explicitly destroyed as a part of destroying the object containing the union just as any object stored in a union much be explicitly constructed as a part of constructing the object containing the union.
It is up to the programmer to have some mechanism to track what, if anything, is stored in the union and to properly handle any construction and destruction actions.
It is up to the programmer to properly access what is stored within a union by knowing what is stored in the union and then using the appropriate union member to access that object.
Program demonstrating using a union
Starting with the source from other answers, I expanded that source to demonstrate the difference between a union member of a struct and a struct member of a struct.
In the code below I have for the union containing class u constructors for both the yes class and the no class. In addition I have a default constructor, copy constructor, and an assignment operator for the class u.
In addition to the two structs you provided, yes and no, I have also provided a third struct, place, which is not used in the union but is instead just another member variable.
Each of the various classes/structs have a static counter to count up each time one is constructed.
I have provided the assignment operator for the struct of u in order to properly handle the assignment. The default assignment operator amounted to a bitwise copy and I wanted to preserve the unique identifier for each instance in order for the tracking of construction and destruction to be properly documented.
union vs struct and containing object construction/destruction
Notice that in the output generated by this program, when the class containing the union is constructed, the place struct is constructed however none of the union members are constructed. And when the class containing the union is destructed, the place struct is destructed however none of the union members are destructed.
You can also see in the output where temporary variables are constructed, used, and then destructed. However notice that when a temporary is assigned into the union, the destructor is never called.
So in the program there is this simple scope with a couple of statements.
{
std::cout << "scope in" << std::endl;
std::cout << " create uu from n" << std::endl;
u uu(n);
std::cout << " assign y to uu" << std::endl;
uu = y;
std::cout << " assign n to uu" << std::endl;
uu = n;
std::cout << "scope out" << std::endl;
}
And the section of output which I have annotated with a few notes corresponding to this section of source is:
scope in
create uu from n
no-cc : 0 -> 1 --- create a temporary struct no for the call to construct
no-cc : 1 -> 2 --- assignment of temporary to union through copy construction
place-c : 1 --- construct non-union struct member
u-c - no: 1 place 1 tu: 2 tu_no: 2
no-d : 1 --- destruct the temporary passed to the constructor
assign y to uu
yes-cc : 0 -> 1 --- create a temporary struct yes for the assignment
yes-cc : 1 -> 2 --- assignment of temporary to union through copy construction
place-c : 2 --- construct non-union struct member
u-c - yes: 2 place 2 tu: 1 tu_yes: 2 --- construct temporary union member using yes version of the constructors
yes-d: 1 --- destruct the temporary used in assignment
u-= : 2 into 1 o.place 2 tu: 1 tu_yes: 2
u-d : 2 tu: 1 tu_yes: 2 --- destruct temporary union struct
place-d : 2 --- destruct non-union struct member
assign n to uu
no-cc : 0 -> 3 --- create a temporary struct no for the assignment
no-cc : 3 -> 4 --- assignment of temporary to union through copy construction
place-c : 3
u-c - no: 3 place 3 tu: 2 tu_no: 4 --- construct temporary union using no version of constructors
no-d : 3 --- destruct the struct no temporary
u-= : 3 into 1 o.place 3 tu: 2 tu_no: 4
u-d : 3 tu: 2 tu_no: 4 --- destruct the struct union temporary
place-d : 3
scope out
followed by the destruction of the instance of u created within the scope as the variable goes out of scope with:
u-d : 1 tu: 2 tu_no: 2
place-d : 1
Notice that we have an instance of no, instance identifier 2, and an instance of yes, instance identifier 2, that we never see a destructor message. These are the temporaries actually stored into the union. Notice that every instance of the member place is destructed as its container of u is destructed.
Source code and output for union example
The source code for this demo program:
// union_test.cpp : This file contains the 'main' function. Program execution begins and ends there.
// https://stackoverflow.com/questions/54723339/how-does-c-union-know-the-type-stored-in-it-and-which-destructor-to-call
#include "pch.h"
#include <iostream>
struct yes {
yes() : n(nc++) { std::cout << " yes-c: " << n << std::endl; }
yes(yes const& o) : n(nc++)
{
std::cout << " yes-cc : " << o.n << " -> " << n << '\n';
}
~yes() { std::cout << " yes-d: " << n << std::endl; }
int n = 0; // identifier number for specific instance
static int nc; // count of number of instances of this class created, instance identifier
};
struct no {
no() : n(nc++) { std::cout << " no-c : " << n << std::endl; }
no(no const& o) : n(nc++)
{
std::cout << " no-cc : " << o.n << " -> " << n << '\n';
}
~no() { std::cout << " no-d : " << n << std::endl; }
int n = 0; // identifier number for specific instance
static int nc; // count of number of instances of this class created, instance identifier
};
struct place {
place() : n(nc++) { std::cout << " place-c : " << n << std::endl; }
place(no const& o) : n(nc++)
{
std::cout << " place-cc : " << o.n << " -> " << n << '\n';
}
~place() { std::cout << " place-d : " << n << std::endl; }
int n = 0; // identifier number for specific instance
static int nc; // count of number of instances of this class created, instance identifier
};
struct u {
union {
yes y;
no n;
};
place p; // non-union to see construction/destruction of non-union.
int nu = 0; // identifier number for specific instance
int tu = 0; // indicator as to type currently in the union
static int nc; // count of number of instances of this class created, instance identifier
u() : nu(nc++) { std::cout << " u-c : " << nu << " place " << p.n << " tu: " << tu << std::endl; }
u(yes _y) :y(_y), nu(nc++), tu(1) {
std::cout << " u-c - yes: " << nu << " place " << p.n << " tu: " << tu;
switch (tu) {
case 0:
std::cout << std::endl;
break;
case 1:
std::cout << " tu_yes: " << y.n << std::endl;
break;
case 2:
std::cout << " tu_no: " << y.n << std::endl;
break;
default:
std::cout << "Unknown tu: " << tu << std::endl;
break;
}
}
u(no _n) :n(_n), nu(nc++), tu(2) {
std::cout << " u-c - no: " << nu << " place " << p.n << " tu: " << tu;
switch (tu) {
case 0:
std::cout << std::endl;
break;
case 1:
std::cout << " tu_yes: " << y.n << std::endl;
break;
case 2:
std::cout << " tu_no: " << y.n << std::endl;
break;
default:
std::cout << "Unknown tu: " << tu << std::endl;
break;
}
}
u(u const& o) : nu(nc++), tu(o.tu)
{
std::cout << "u-cc : " << o.nu << " -> " << nu << " tu: " << tu << " o.place " << o.p.n;
switch (tu) {
case 0:
std::cout << std::endl;
break;
case 1:
std::cout << " tu_yes: " << o.y.n << std::endl;
break;
case 2:
std::cout << " tu_no: " << o.n.n << std::endl;
break;
default:
std::cout << "Unknown tu: " << tu << std::endl;
break;
}
}
u & operator = (const u & o)
{
std::cout << "u-= : " << o.nu << " into " << nu << " o.place " << o.p.n << " tu: " << o.tu;
tu = o.tu;
switch (o.tu) {
case 0:
std::cout << std::endl;
break;
case 1:
std::cout << " tu_yes: " << o.y.n << std::endl;
break;
case 2:
std::cout << " tu_no: " << o.n.n << std::endl;
break;
default:
std::cout << "Unknown tu: " << tu << std::endl;
break;
}
return *this;
}
~u() {
std::cout << " u-d : " << nu << " tu: " << tu;
switch (tu) {
case 0:
std::cout << std::endl;
break;
case 1:
std::cout << " tu_yes: " << y.n << std::endl;
break;
case 2:
std::cout << " tu_no: " << n.n << std::endl;
break;
default:
std::cout << "Unknown" << std::endl;
break;
}
}
};
int yes::nc = 0;
int no::nc = 0;
int place::nc = 0;
int u::nc = 0;
int main()
{
std::cout << "create uu" << std::endl;
u xx;
std::cout << "create y" << std::endl;
yes y;
std::cout << "create n" << std::endl;
no n;
{
std::cout << "scope in" << std::endl;
std::cout << " create uu from n" << std::endl;
u uu(n);
std::cout << " assign y to uu" << std::endl;
uu = y;
std::cout << " assign n to uu" << std::endl;
uu = n;
std::cout << "scope out" << std::endl;
}
xx = y;
return 0;
}
And the output generated by this program.
create uu
place-c : 0
u-c : 0 place 0 tu: 0
create y
yes-c: 0
create n
no-c : 0
scope in
create uu from n
no-cc : 0 -> 1
no-cc : 1 -> 2
place-c : 1
u-c - no: 1 place 1 tu: 2 tu_no: 2
no-d : 1
assign y to uu
yes-cc : 0 -> 1
yes-cc : 1 -> 2
place-c : 2
u-c - yes: 2 place 2 tu: 1 tu_yes: 2
yes-d: 1
u-= : 2 into 1 o.place 2 tu: 1 tu_yes: 2
u-d : 2 tu: 1 tu_yes: 2
place-d : 2
assign n to uu
no-cc : 0 -> 3
no-cc : 3 -> 4
place-c : 3
u-c - no: 3 place 3 tu: 2 tu_no: 4
no-d : 3
u-= : 3 into 1 o.place 3 tu: 2 tu_no: 4
u-d : 3 tu: 2 tu_no: 4
place-d : 3
scope out
u-d : 1 tu: 2 tu_no: 2
place-d : 1
yes-cc : 0 -> 3
yes-cc : 3 -> 4
place-c : 4
u-c - yes: 4 place 4 tu: 1 tu_yes: 4
yes-d: 3
u-= : 4 into 0 o.place 4 tu: 1 tu_yes: 4
u-d : 4 tu: 1 tu_yes: 4
place-d : 4
no-d : 0
yes-d: 0
u-d : 0 tu: 1 tu_yes: -858993460
place-d : 0
I've noticed some behaviour which I can't understand in parameterized constructors. Given the following program:
#include <iostream>
using namespace std;
class A {
public:
int x;
A() {}
A(int i) : x(i){
cout << "A\n";
}
~A(){
cout << "dA\n";
}
};
int main(){
A p;
p = 3;
cout << p.x << endl;
p = 5;
cout << p.x << endl;
return 0;
}
I get as output:
A
dA
3
A
dA
5
dA
This means that using = triggers the parameterized constructor, destroys the object on which it's called and creates a new object.
I cannot understand this behaviour and I can't find the answer in the standard ( I am sure it is there somewhere, but it may be stated in a sophisticated way). Could someone help me with an explanation?
The phrase you're probably looking for is "implicit conversion".
If you add a copy constructor and an assignment operator, and then give each object a unique ID, it's easier to see where things go:
int counter = 0;
class A {
public:
int id;
A(): id(++counter) {cout << "A(): " << id << "\n";}
A(int i) : id(++counter) {cout << "A(" << i << "): " << id << "\n";}
// Don't copy the id.
// (This isn't used anywhere, but you can't see that it's not used unless it exists.)
A(const A& a) : id(++counter) {cout << "A(" << a.id << "): " << id << "\n";}
// Don't copy the id here either.
A& operator=(const A&a) {cout << id << " = " << a.id << "\n"; return *this;}
~A(){cout << "destroy: " << id << "\n";}
};
int main(){
A p;
cout << "p is " << p.id << "\n";
p = 3;
cout << "p is " << p.id << "\n";
p = 5;
cout << p.id << "\n";
}
Output:
A(): 1
p is 1
A(3): 2
1 = 2
destroy: 2
p is 1
A(5): 3
1 = 3
destroy: 3
1
destroy: 1
As you can see, the parameterized constructor is used to create a temporary object whose value can be assigned to p, and that temporary is destroyed immediately after that.
You can also see that p is alive and well until the very end.
With a statement like
p = 3;
what you're actually doing is
p = A(3);
which really translates to
p.operator=(A(3));
The temporary A object created by A(3) of course needs to be destructed, it is temporary after all.
The object p itself will not be destructed by the assignment.
I have been trying to understand how the clear() function in std::vector works, I am trying to emulate the workings of a std::vector.
So far, I have learned that clear() destroys all the objects but retains the capacity of the vector.
The bit I don't understand is how does the destructor of the objects in vector are called.
class A {
public:
A(int a) {
m_a = a;
cout << "Constructed object number: " << a << endl;
}
~A() {
cout << "Destructed object number: " << m_a << endl;
}
int m_a;
};
int main() {
char** memory = new char*[100];
A t1(1);
memory[sizeof(A)*10] = reinterpret_cast<char *>(&t1);
A* t = reinterpret_cast<A*>(memory[sizeof(A)*10]);
cout << t->m_a << endl;
//Trying to figure out on how to clear the vector.
memory[sizeof(A)*10] = NULL;
//Testing on how the destructor is getting called
vector<A*> vec;
vec.push_back(&A(2)); // I know it is wrong, just here for understanding purposes.
A t2(3);
vec.push_back(&t2);
cout << "Clear" << endl;
vec.clear();
cout << "End" << endl;
return 0;
}
Clearing the vector is not calling the destructor of "t2", as it is a pointer here, but if I store objects, than destructor of "t2" is getting called in clear function.
This is only for understanding purposes as to how the std::vector actually works.
#Anurag one very important thing regarding STL container: it's keep the copy of the object/pointer/primitive type irrespective of the container type(sequence containers, associative containers and unordered container).
Now come back to your doubt 1. with object and 2. With pointer , below I am explaining with example :
Example Of Object type (See the output, you will be clear):
#include <iostream>
#include<vector>
using namespace std;
class Achintya {
static int counter ;
static int i ;
public:
Achintya() {
cout<<"Achintya Constructor called : " << ++i <<endl;
}
~Achintya() {
cout<<"Achintya destructor called : " << ++counter <<endl;
}
Achintya(const Achintya&) {
cout<<"Achintya copy constructor called : "<<endl;
}
};
int Achintya:: i;
int Achintya:: counter;
int main() {
vector<Achintya> VecAchintya;
Achintya a1;
// Address of the pointer
cout << " 1st object address : " <<&a1 <<endl;
//Push back to vector
// it is just a copy and store in vector (copy constructor is begin invoke )
VecAchintya.push_back(a1);
cout << " =============================================" << endl;
cout<< " Number of element present in vector is : " << VecAchintya.size() << endl;
cout << " =============================================" << endl;
// cli is not iterator
for(auto& cli:VecAchintya ) {
cout << " Adress of 1st element is : " << &cli <<endl;
}
// it clear the object it self which is being created at the time of the push_back()
VecAchintya.clear();
cout << " =============================================" << endl;
cout<< " Number of element present in vector is : " << VecAchintya.size() << endl;
cout << " =============================================" << endl;
}
output ::
Achintya Constructor called : 1
1st object address : 0x7ffd70ad339f
Achintya copy constructor called :
=============================================
Number of element present in vector is : 1
=============================================
Adress of 1st element is : 0x23c6c30
Achintya destructor called : 1
=============================================
Number of element present in vector is : 0
=============================================
Achintya destructor called : 2
Example Of pointer type (See the output, you will be clear):
#include <iostream>
#include<vector>
using namespace std;
class Achintya {
static int counter ;
static int i ;
public:
Achintya() {
cout<<"Achintya Constructor called : " << ++i <<endl;
}
~Achintya() {
cout<<"Achintya destructor called : " << ++counter <<endl;
}
Achintya(const Achintya&) {
cout<<"Achintya copy constructor called : "<<endl;
}
};
int Achintya:: i;
int Achintya:: counter;
int main() {
vector<Achintya *> VecAchintya;
Achintya* a1 = new Achintya();
// Address of the pointer
cout << " 1st object address : " << a1 <<endl;
//Push back to vector
// it is just a copy the pointer value and store
VecAchintya.push_back(a1);
cout << " =============================================" << endl;
cout<< " Number of element present in vector is : " << VecAchintya.size() << endl;
cout << " =============================================" << endl;
// cli is not iterator
for(auto& cli:VecAchintya ) {
cout << " Adress of 1st element is : " << cli <<endl;
}
// it clear the pointer it self which is being stored at the time push_back()
VecAchintya.clear();
cout << " =============================================" << endl;
cout<< " Number of element present in vector is : " << VecAchintya.size() << endl;
cout << " =============================================" << endl;
// call destructor explicitly
delete a1;
}
Output ::
Achintya Constructor called : 1
1st object address : 0x2533c20
=============================================
Number of element present in vector is : 1
=============================================
Adress of 1st element is : 0x2533c20
=============================================
Number of element present in vector is : 0
=============================================
Achintya destructor called : 1
You may find it easier to study pop_back and think of resize down and clear as special multi-pop calls. You could certainly implement them that way in your own implementation.
I am working on an Entity class which needs to provide access to its data members through setter methods, which check that a value is allowed before storing it (checking code not shown). The Select class is one of the types stored and it needs to do some very specific cleaning up when destroyed:
#include<memory>
#include <iostream>
using namespace std;
class Select {
int i, j;
friend class Entity;
public:
Select(int a, int b) : i{a}, j{b} { }
~Select() {
cout << "destroying " << i << j << endl;
}
protected:
Select() { };
};
class Entity {
Select select_;
public:
void select_setter(const Select &select) {
cout << "to be assigned... " << select.i << select.j << endl;
select_ = select;
}
static shared_ptr<Entity> create(const Select &s) {
auto sp = make_shared<Entity>(Entity{});
sp->select_setter(s);
return sp;
}
};
This block demonstrates how I want the Entity type to be used:
int main() {
auto sp = Entity::create({1, 1});
sp->select_setter({2, 2});
sp->select_setter({3, 3});
cout << "the end" << endl;
return 0;
}
Here is the output:
destroying 00
to be assigned... 11
destroying 11
to be assigned... 22
destroying 22
to be assigned... 33
destroying 33
the end
destroying 33
Why is 33 destroyed twice, but 00, 11 and 22 only once?
When you want to analyse such constructor/destructor behaviour, my recommendation is to:
Log all constructors (default constructor, copy constructor, additional constructor)
Log all destructors
Log address of the objects to know who is who...
By adding more outputs, and implementing Select and Entity copy constructors you can get:
Before auto sp = Entity::create({1, 1});
In Select 1 1 ctor 0x7fff85e31b80 // That's Select temporary object {1,1} being created
In Select default ctor 0x7fff85e31b40 // That's Entity attribute being created before Entity ctor is entered below
In Entity ctor 0x7fff85e31b40 // That's Entity ctor for Entity{} you create
In Select default ctor 0x2248028 // That's Entity attribute being created before Entity ctor is entered below
In Entity copy ctor copy 0x7fff85e31b40 to 0x2248028 // That's copying Entity{} object as the shared_ptr attribute
In Entity dtor 0x7fff85e31b40 // That's Entity{} being destroyed
destroying 00 0x7fff85e31b40 // That's Entity{}'s Select attribute being destroyed. Is 00 but could be anything else (not initialized)
to be assigned... 11
destroying 11 0x7fff85e31b80 // That's Select temporary object {1,1} being destroyed
Before sp->select_setter({2, 2});
In Select 2 2 ctor 0x7fff85e31b70 // That's Select temporary object ({2,2})
to be assigned... 22
destroying 22 0x7fff85e31b70 // That's Select temporary object ({2,2}) being destroyed
Before sp->select_setter({3, 3});
In Select 3 3 ctor 0x7fff85e31b80 // That's Select temporary object ({3,3})
to be assigned... 33
destroying 33 0x7fff85e31b80 // That's Select temporary object ({2,2}) being destroyed
the end
In Entity dtor 0x2248028 // That's Entity stored in the shared_ptr object being destroyed
destroying 33 0x2248028
And this perfectly makes sense...
Simply use this code:
#include <memory>
#include <iostream>
using namespace std;
class Select {
int i, j;
friend class Entity;
public:
Select(int a, int b) : i{a}, j{b} { std::cout << "In Select " << a << " " << b << " ctor" << std::hex << "0x" << this << std::endl;}
~Select() {
cout << "destroying " << i << j << std::hex << "0x" << this << endl;
}
Select( const Select& e ) {
i = e.i; j = e.j;
std::cout << "In Select copy ctor copy " << std::hex << "0x" << &e << " to " << std::hex << "0x" << this << std::endl;
}
Select() { std::cout << "In Select default ctor" << std::hex << "0x" << this << std::endl; }
};
class Entity {
Select select_;
public:
Entity() { std::cout << "In Entity ctor " << std::hex << "0x" << this << std::endl; }
Entity( const Entity& e ) {
select_ = e.select_;
std::cout << "In Entity copy ctor copy " << std::hex << "0x" << &e << " to " << std::hex << "0x" << this << std::endl; }
~Entity() { std::cout << "In Entity dtor " << std::hex << "0x" << this << std::endl; }
void select_setter(const Select &select) {
cout << "to be assigned... " << select.i << select.j << endl;
select_ = select;
}
static shared_ptr<Entity> create(const Select &s) {
auto sp = make_shared<Entity>(Entity{});
sp->select_setter(s);
return sp;
}
};
int main() {
std::cout << "Before auto sp = Entity::create({1, 1});" << std::endl;
auto sp = Entity::create({1, 1});
std::cout << "Before sp->select_setter({2, 2});" << std::endl;
sp->select_setter({2, 2});
std::cout << "Before sp->select_setter({3, 3});" << std::endl;
sp->select_setter({3, 3});
cout << "the end" << endl;
return 0;
}