Why "vector.erase()" (in C++) is not behaving as expected? - c++

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

Related

Wrong destructor called by vector erase

I have a vector with some objects.
I noticed that when I remove one element from vector with erase method, I get the destructor call to the wrong element (always to the last element).
Here a minimal example that produce a bad output.
// Example program
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Test {
public:
Test(string value) {
_value = value;
cout << "Constructor:" << _value << endl;
}
Test(const Test &t) {
_value = t._value;
cout << "Copied:" << _value << endl;
}
~Test() {
cout << "Destructor:" << _value << endl;
}
string print() {
return _value;
}
string _value;
};
int main()
{
vector<Test> vec;
vec.reserve(3);
cout << "Creation" << endl << endl;
vec.push_back(Test("1"));
vec.push_back(Test("2"));
vec.push_back(Test("3"));
cout << endl << "Deleting" << endl << endl;
vec.erase(vec.begin()); // Here is called the element with value "3"
vec.erase(vec.begin()); // Here is called the element with value "3"
cout << endl << "Log" << endl << endl;
// But the final log print "3"
for (unsigned i = 0; i < vec.size(); i++) {
cout << vec[i].print()<<endl;
}
return 0;
}
The output is:
Creation
Constructor:1
Copied:1
Destructor:1
Constructor:2
Copied:2
Destructor:2
Constructor:3
Copied:3
Destructor:3
Deleting
Destructor:3 <-- WRONG, NEED TO BE 1
Destructor:3 <-- WRONG, NEED TO BE 2
Log
3
Destructor:3
I would solve this problem without change the container vector.
vec.erase(vec.begin()); does not destruct the first element. It overwrites it by shifting all of the subsequent ones by one place, using either the move- or copy-assignment operator. What remains of the last element after it has been moved from is then destructed, which is what you're observing.

Std::vector not changing data inside class

I'm doing some stuff with vectors and I'm trying to write a program that can loop through and perform operations on objects inside a vector. I've read a lot about minimal code so I've programmed this little application to illustrate the problem.
The output of this program is:
Element 1 is equal to 2
Element 2 is equal to 4
Element 1 is equal to 2
Element 2 is equal to 4
Press any key to continue...
The output should be:
Element 1 is equal to 2
Element 2 is equal to 4
Element 1 is equal to 7
Element 2 is equal to 9
Press any key to continue...
Why am I not achieving the desired output?
#include <vector>
#include <iostream>
// Some object
class A {
public:
A(int data)
: data(data)
{}
int data;
};
class B {
public:
// A function that adds a new 'A' object to the vector in class b
void push_back_to_vector(A &element);
// A function that changes the data of the objects stored in vector_of_a
void add_to_vector();
// A vector to hold objects of A
std::vector<A> vector_of_a;
};
void B::push_back_to_vector(A &element) {
vector_of_a.push_back(element);
}
void B::add_to_vector() {
for (size_t i = 0; i < vector_of_a.size(); i++) {
// Add five to all the elements in the vector
vector_of_a[i].data += 5;
}
}
int main() {
A element_1(2);
A element_2(4);
B b;
b.push_back_to_vector(element_1);
b.push_back_to_vector(element_2);
std::cout << "Element 1 is equal to " << element_1.data << std::endl;
std::cout << "Element 2 is equal to " << element_2.data << std::endl;
// This should change element_1.data to 7 and element_2.data to 9
b.add_to_vector();
std::cout << "Element 1 is equal to " << element_1.data << std::endl;
std::cout << "Element 2 is equal to " << element_2.data << std::endl;
system("pause");
return 0;
}
The push_back copies new elements. If you want to do what you seem to want to do, you need to store pointers to A, not just A.
Basically, your code is just this:
A element_1(2);
A element_2(4);
std::cout << "Element 1 is equal to " << element_1.data << std::endl;
std::cout << "Element 2 is equal to " << element_2.data << std::endl;
std::cout << "Element 1 is equal to " << element_1.data << std::endl;
std::cout << "Element 2 is equal to " << element_2.data << std::endl;
system("pause");
return 0;
Everything related to B doesn't work. Try this:
class B {
public:
// A function that adds a new 'A' object to the vector in class b
void push_back_to_vector(A *element);
// A function that changes the data of the objects stored in vector_of_a
void add_to_vector();
// A vector to hold objects of A
std::vector<*A> vector_of_a;
};
void B::push_back_to_vector(A* element) {
vector_of_a.push_back(element);
}
void B::add_to_vector() {
for (size_t i = 0; i < vector_of_a.size(); i++) {
// Add five to all the elements in the vector
vector_of_a[i]->data += 5;
}
}
And pass pointers:
b.push_back_to_vector(&element_1);
b.push_back_to_vector(&element_2);
Of course, be aware of the lifetime of the objects on the stack that you are pointing to in B.

Implementation of std::vector clear function

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.

How to explain that there are 9 times of destruction?

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
*****************/

map contains value as a list+how to print in C++

I have a map having strings as keys and lists of file names as values.
ex: Map(firstDir, list(file1,file2,file3))
I know that by using following code I can print value which is of String
{
cout << "Key: " << pos->first << endl;
cout << "Value:" << pos->second << endl;
}
But if pos->second contains a List, how to display?
overload operator << for list
std::ostream& operator << (std::ostream& out, std::list<ListElemType> const& lst)
{
for(std::list<ListElemType>::iterator it = lst.begin(); it != lst.end(); ++it)
{
if(it != lst.begin())
out << /*your delimiter*/;
out << *it;
}
return out;
}
now you can do what you want
cout << "Key: " << pos->first << endl << "Value:" << pos->second << endl;
How about using Boost.Foreach ?
#include <boost/foreach.hpp>
{
cout << "Key: " << pos->first << endl;
cout << "Values:" << endl;
BOOST_FOREACH(std::string const& file, pos->second)
{
cout << "\t" << file << endl;
}
}
The first thing you would have to decide is how do you want the list to be displayed? Separated by commas or each entry in a new line perhaps? Then, you could overload the stream output operator for lists of strings:
std::ostream & operator<<(std::ostream & stream, const std::list<std::string> & object) {
std::copy(object.begin(), object.end(), std::ostream_iterator<std::string>(std::cout, ", ")
}
This operator will get called everytime you write am std::list<std::string> to any output stream, and it will print the values of the list separated by commas.
Use a library which overloads stream inserters for containers, such as my example:
void example(MapType const &m) {
using namespace kniht::container_inserters; // must be enabled in this scope
MapType::const_iterator x = m.begin();
cout << *x << '\n'; // can print the pair directly
cout << "Key: " << x->first << '\n'; // or format it yourself
cout << "Value: " << x->second << '\n';
// output for a list: [a, b, c]
}
You can extract the used functions from my header or simply copy it elsewere (it's self-contained, but does have other utilities).
Since you are using STL the most simple way to print such structure is the next one:
#include <iostream>
#include <map>
#include <list>
#include <string>
using namespace std;
int main()
{
map<string, list<string>> sample;
... //fill the map
for (auto itr : sample){
cout << itr.first << ":\t" << endl;
for (auto innerItr : sample.second)
cout << innerItr << " ";
cout << endl;
}
}
What you want to be able to do though is to create a template that can output any printable sequence, i.e. a sequence of printable items. You also want to be able to customise how you start, end and delimit the sequence.
So create your own wrapper class. Now because it is your wrapper you can overload operator<< happily on it without interfering with an external namespace.
You could use a boost::range in its constructor, or a templated being/end sequence or a templated collection.
Also take as parameters your delimiters. You can have reasonable defaults if you want though,
Finally when you wish to print one, you just do something like:
std::cout << MySequenceFormatter( seq.begin(), seq.end(), delim, startofSeq, endOfSeq ) << std::endl;
When outputting maps, you can use a boost::transform_iterator in between to transform each std::pair as you iterate through the sequence. Then you can have you own pair formatter too.
In fact you can do this for any sequence where you wish to use a custom method to print the items themselves.
Strait forward version (-:
#include <map>
#include <list>
#include <string>
#include <iostream>
#include <sstream> // only for generating testdata
typedef std::list<std::string> TStringList;
typedef std::map<std::string, TStringList> TStringListMap;
TStringListMap myMap;
int main()
{
// Generating testdata
for (int i=0;i<10;i++)
{
std::stringstream kstr;
kstr << i;
std::string key = kstr.str();
for (int ii=0;ii<=i;ii++)
{
std::stringstream vstr;
vstr << ii;
myMap[key].push_back( vstr.str() );
}
}
//Print map
for ( TStringListMap::const_iterator it = myMap.begin(), end = myMap.end(); it != end; ++it )
{
std::cout << it->first<< ": ";
for( TStringList::const_iterator lit = it->second.begin(), lend = it->second.end(); lit != lend; ++lit )
{
std::cout << *lit << " ";
}
std::cout << std::endl;
}
return 0;
}
Output:
0: 0
1: 0 1
2: 0 1 2
3: 0 1 2 3
4: 0 1 2 3 4
5: 0 1 2 3 4 5
6: 0 1 2 3 4 5 6
7: 0 1 2 3 4 5 6 7
8: 0 1 2 3 4 5 6 7 8
9: 0 1 2 3 4 5 6 7 8 9