Std::vector not changing data inside class - c++

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.

Related

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

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

splitting one vector into almost-equal sub vectors then storing into another vector

I am trying to split one large user given vector into x sub vectors. Everything "seems" to work as it should but the outcome is not right.
std::vector<std::vector<std::string>> split_to_sub_vectors(std::vector<std::string> initial_vector, int thread_amount) {
std::cout << "initial size: " << initial_vector.size() << std::endl;
int size_for_splitting = initial_vector.size();
std::cout << "split amount: " << thread_amount << std::endl;
int r = size_for_splitting / thread_amount;
std::cout << r << " need to be in each sub-vector" << std::endl;
std::cout << "There will be: " << size_for_splitting % thread_amount << " element remaining" << std::endl;
std::vector<std::vector<std::string>> perm_vector;
for (int x = 0; x < thread_amount; x++) {
std::vector<std::string> temp_vector;
for (int a = 0; a < r; a++) {
hm++;
std::cout << hm << std::endl;
temp_vector.push_back(initial_vector[hm]);
}
perm_vector.push_back(temp_vector);
}
std::cout << "Size of vector holding the sub vectors after splitting: " << perm_vector.size() << std::endl;
std::cout << perm_vector[0][0];
return perm_vector;
Running this code will give you this:
initial size: 7
split amount: 3
2 need to be in each sub-vector
There will be: 1 element remaining
1
2
3
4
5
6
Size of vector holding the sub vectors after splitting: 3
2
the vector i pass in is called test holds strings and is like so:
test.push_back("1");
test.push_back("2");
test.push_back("3");
test.push_back("4");
test.push_back("5");
test.push_back("6");
test.push_back("7");
Everything up until the last print statement seems to work. So perm_vector should hold 3 sub vectors containing every element in the main user given vector. When you print perm_vector[0][0] you would expect the output to be "1", but it is 2, also 7 should not be in the vector and 6 should be the last one but since it starts at 2, 7 is in it. the counter is defined outside of the function and it starts at 0. My question is why is it starting at 2?
I see two problems in your code:
hm is incremented before use. Furthermore, there is no point in making it global.
size_for_splitting is the result of an integer division, so the remainder is missing
I modified your code so the issues with hm are solved. I get the intended output <<1, 2>, <3, 4>, <5, 6>>, the 7 is missing as mentioned above.
#include <iostream>
#include<vector>
#include<string>
std::vector<std::vector<std::string> > split_to_sub_vectors(std::vector<std::string> initial_vector, int thread_amount) {
std::cout << "initial size: " << initial_vector.size() << std::endl;
int size_for_splitting = initial_vector.size();
std::cout << "split amount: " << thread_amount << std::endl;
int r = size_for_splitting / thread_amount;
std::cout << r << " need to be in each sub-vector" << std::endl;
std::cout << "There will be: " << size_for_splitting % thread_amount << " element remaining" << std::endl;
std::vector<std::vector<std::string> > perm_vector;
int hm = 0;
for (int x = 0; x < thread_amount; x++) {
std::vector<std::string> temp_vector;
for (int a = 0; a < r; a++) {
std::cout << hm << std::endl;
temp_vector.push_back(initial_vector[hm]);
hm++;
}
perm_vector.push_back(temp_vector);
}
std::cout << "Size of vector holding the sub vectors after splitting: " << perm_vector.size() << std::endl;
return perm_vector;
}
int main()
{
std::vector<std::string> test;
test.push_back("1");
test.push_back("2");
test.push_back("3");
test.push_back("4");
test.push_back("5");
test.push_back("6");
test.push_back("7");
std::vector<std::vector<std::string> > out = split_to_sub_vectors(test, 3);
}
Even if hm starts at 0, you increment it before you use it. Probably if you increment at the end of the internal for loop, you might get the output you expect. It's hard to tell the problem because I don't know what's in 'initial_vector', I assume initial_vector[0] = 1?
If you use the range-v3 library, implementing this logic becomes much easier, and less error prone:
#include <range/v3/all.hpp>
namespace rs = ranges;
namespace rv = ranges::views;
auto split_to_sub_vectors(std::vector<std::string> initial_vector, int thread_amount) {
auto res = initial_vector
| rv::chunk(thread_amount)
| rs::to<std::vector<std::vector<std::string>>>;
if (res.back().size() != thread_amount)
res.pop_back();
return res;
}
Here's a demo.

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.

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.

garbage values for vector push_back

I'm trying to assign an array's values to a vector. It seems to be working fine for one vector, but when I do it for a second, I'm getting back garbage values. I cout the number and I know it's correct, but it's not assigning correctly. I don't understand because it's working fine for the first vector.
int sorted[] = {0,1,2,3,4,5,6,7,8,9,10};
// make two smaller arrays, do this untill they are a base case size;
void split(int *dataIN, int dataSize){
// new data will be broken up into two vectors with each half of the
// original array. These will be size firstHalfSize and secondHalfSize.
int firstHalfSize;
int secondHalfSize;
vector<int> firstHalf;
vector<int> secondHalf;
// test to see if array in is odd or even
bool isOdd;
if (dataSize%2 == 1){
isOdd = true;
}else if (dataSize%2 == 0){
isOdd = false;
}
// determine length of new vectors
// second half is firstHalf + 1 if odd.
firstHalfSize = dataSize/2;
if (isOdd){
secondHalfSize = firstHalfSize + 1;
}else if (!isOdd){
secondHalfSize = firstHalfSize;
}
// assign first half of dataIn[] to firstHalf vector
cout << "firs: " << firstHalfSize << endl;
for (int i = 0; i < firstHalfSize; i++){
cout << "a: " << dataIN[i] << endl;// make sure i have the right number
firstHalf.push_back(dataIN[i]);// assign
cout << "v: " << firstHalf[i] << endl;// make sure assigned correctly
}
// do the same for second half
cout << "second: " << secondHalfSize << endl;
for (int i = firstHalfSize; i < (firstHalfSize+secondHalfSize); i++){
cout << "a: " << dataIN[i] << endl;
secondHalf.push_back(dataIN[i]);
cout << "v: " << secondHalf[i] << endl;
}
}
int main(void){
split(sorted, sizeof(sorted)/sizeof(int));
return 0;
}
This is my result. As you can see the first vector push_back went fine and the array values (after "a: ") are also correct.
firs: 5
a: 0
v: 0
a: 1
v: 1
a: 2
v: 2
a: 3
v: 3
a: 4
v: 4
second: 6
a: 5
v: -805306368
a: 6
v: 2
a: 7
v: -805306368
a: 8
v: 0
a: 9
v: 0
a: 10
v: 0
In the second case, you are indexing from firstHalfSize.
You need to cout the values starting from index 0. For example:
cout << "v: " << secondHalf[i-firstHalfSize] << endl;
You are iterating firstHalf from 0 to firstHalfSize with the variable i, so i will be within the range of firstHalf, when you use operator[] - in the second vector's case, i does not mean the same thing.
The filling of the vector is working. It is just your debug output that is incorrect. When outputting values from secondHalf you need to use indexes from 0, not from firstHalfSize.
You can write your code more simply if you take advantage of the std::vector range constructor that takes a pair of iterators. Array pointers can be treated as iterators:
void print(const std::vector<int>& data){
for(int value : data)
std::cout << value << " ";
std::cout << "\n";
}
void split(int *dataIN, int dataSize){
auto firstHalfSize = (dataSize + 1) / 2;
std::vector<int> firstHalf(dataIN, dataIN + firstHalfSize);
std::vector<int> secondHalf(dataIN + firstHalfSize, dataIN + dataSize);
std::cout << "firstHalf: ";
print(firstHalf);
std::cout << "seconHalf: ";
print(secondHalf);
}
Live demo