C++ : Size of the array didn't change even after deleting it - c++

Assuming I have this program below
The size of the array is 3 and basically when deleting it and then try to print the size again , I still get the value 3 .
Why is that ?
#include <vector>
#include <iostream>
int main(){
std::vector<int*> v;
v.push_back(new int{1});
v.push_back(new int{2});
v.push_back(new int{3});
std::cout << v.size() << '\n';
for (auto iptr : v){
delete iptr;
}
std::cout << v.size() << '\n';
return 0;
}

Because you deleted whatever was pointed by the pointer
vector<CPerson *> Persons;
As far as the vector is concerned, it still has 3 pointers pointing somewhere.Beware, you are not permitted to dereference them pointers, otherwise you will invoke undefined behavior).

You have a vector of pointers. Using delete on a pointer in that vector will delete the thing the pointer is pointing at. It does not remove the pointer itself from the vector, and so does not change the size of the vector. Why should it? How could it?
Here the code you were looking for (I guess):
for (int i = 0; i < Persons.size(); i++)
{
CPerson *Student = Persons[i];
if (Student)
{
Persons.erase(Persons.begin() + i);
delete Student;
i--;
}
}
Note:
vector::erase is (one way) to remove items from a vector.
The cast is unnecessary, because your base class has a virtual destructor.
Erasing items from a vector while you are iterating through it is tricky because the size of the vector changes when you erase an item and all the later items in the vector move down one place, that why I have i-- to compensate, without that you will at least skip items and at worst iterate off the end of the vector.

When you delete the pointers that are in the vector, it just deletes the memory that you assigned to them with new, it doesn't do anything about the fact that you have a pointer, there for you are cleaning up memory that is outside of the vector, the vector will clean it self up at the end of the scope. (In this case, the end of the function.) Just remember, the vector doesn't clean up your new memory, you have to clean that up.

Related

How do vector elements preserve their original address after a vector std::move?

As you can see in the output, the objects of the vector pre not only "moved" to the vector post, but also preserved their original address space in memory. What is really going on behind this move? Is this behaviour expected? Say I need to have a separate vector of pointers to these objects, is it safe to assume that after this move the objects will always have their original addresses?
Actually, I have a class containing a vector like this and the vector of pointers I mentioned as members. I have also deleted the copy ctors, and defined the move ones for the class.
#include <iostream>
#include <vector>
struct B {
int val = 0;
B(int aInt) : val(aInt) { };
};
int main() {
std::vector<B> pre;
pre.push_back(B(1));
pre.push_back(B(2));
std::cout << "pre-move:\t" << (void*)&pre.at(0) << '\n';
std::cout << "pre-move:\t" << (void*)&pre.at(1) << '\n';
std::vector<B> post(std::move(pre));
std::cout << "post-move:\t" << (void*)&post.at(0) << '\n';
std::cout << "post-move:\t" << (void*)&post.at(1) << '\n';
return 0;
}
Output:
pre-move: 0x1d7b150
pre-move: 0x1d7b154 <------|
post-move: 0x1d7b150 |
post-move: 0x1d7b154 <------|
A vector is basically nothing more than a pointer to heap-allocated memory, the current length and the current capacity of the vector.
By "moving" a vector, all you're doing is copying those values, and resetting the values of the moved-from vector.
For the data of the vector, it's basically equivalent to
original_pointer = some_place_in_memory;
new_pointer = original_pointer; // Copies the *value* of original_pointer
original_pointer = nullptr;
There's no need to allocate new memory and copy the data in the vector.
The whole point of the move operation is to avoid copying the elements, so if they got copied(there is no such thing as truly "moving" the memory) the move would be just a copy.
Vectors are usually implemented as 3 pointers: begin,end and capacity. All point to a dynamically-allocated array. Then moving the vector is just copying those three pointers and so the array and elements just change their owner.
I think it should be safe to assume that pointers to the elements remain valid.
It will be clear, if we write semantically equal code without std::vector:
B* pre = new B[2]; // Declare std::vector<B> and allocate some space to make the following line correct
B[0] = 1; // pre.push_back(B(1));
B[1] = 2; // pre.push_back(B(2));
B* post = pre; // std::vector<B> post(std::move(pre));
Actually, vector move boils down to pointer copying without reallocation. Data which the pointer points at remains in it's place, so addresses of vector elements do not change.
In this code example after the fourth line, both pre and post point to the same data with same address.
std::vector is a wrapper for a pointer to array with some additional functionality. So after doing std::vector<B> post(std::move(pre));, post will contain a pointer with the same value which was in pre.

C++: Remove element from dynamic struct array and shift other elements

I have an array of structs. I am trying to delete a list of elements from that array and shift other elements to the left. After shifting the elements I am trying to delete/free the memory at the end of the array which we don't require anymore. I have the following code:
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
void removeelement(int*);
void displayelements();
typedef struct {
int n;
}element;
element** array;
int numofelements=5;
int main() {
array = (element**)malloc(5*sizeof(element*));
for(int i=0;i<5;i++){
array[i] = new element;
array[i]->n=i;
}
int removelist[3] = {1,3,4};
removeelement(removelist);
displayelements();
return 0;
}
void removeelement(int* removelist){
for(int i=0;i<3;i++){
int index = removelist[i];
int j;
for(j=index;j<numofelements-2;j++){
array[j] = array[j+1];
}
delete [] array[j+1];
numofelements--;
}
}
void displayelements(){
int i=0;
while(i<numofelements){
printf("%d\n",array[i]->n);
i++;
}
}
But delete [] array[j+1]; is causing an exception:
*** Error in `main': double free or corruption (fasttop): 0x0000000001861cb0 ***
I don't understand what's causing this. As many people have suggested in other forums, I am using the 'new' operator to create a new,dynamic element.
EDIT:
I made the following changes:
I changed for(j=index;j<numofelements-2;j++){ to for(j=index;j<numofelements-1;j++){
int index = removelist[i] to int index = removelist[i]-i
I removed delete [] array[j+1] put delete array[numofelements+1] outside both the for loops.
Though I had used delete only on one element, It dealloced memory for the other redundant elements as well, which is interesting.
This is the final code:
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
void removeelement(int*);
void displayelements();
typedef struct {
int n;
}element;
element** array;
int numofelements=5;
int main() {
array = (element**)malloc(5*sizeof(element*));
for(int i=0;i<5;i++){
array[i] = new element;
array[i]->n=i;
}
int removelist[3] = {1,3,4};
removeelement(removelist);
displayelements();
return 0;
}
void removeelement(int* removelist){
for(int i=0;i<3;i++){
int index = removelist[i]-i;
int j=index;
for(;j<numofelements-1;j++){
array[j] = array[j+1];
}
numofelements--;
}
delete array[numofelements+1];
}
void displayelements(){
int i=0;
while(i<5){
printf("%d\n",array[i]->n);
i++;
}
}
I got it working using this code. But I am going to use std::vector as many of you suggested.
You used delete[] expression on a pointer that was not returned by new[] expression. Therefore the behaviour of the program is undefined.
Anything that was allocated with new must be deallocated with delete. delete[] will not do.
Even if you had used the correct expression, there's another bug:
int numofelements=5;
//...
for(int i=0;i<3;i++){
int index = removelist[i];
int j;
for(j=index;j<numofelements-2;j++){
array[j] = array[j+1];
}
delete [] array[j+1];
numofelements--;
}
After the first iteration of the outer loop, array[4] has been deleted. Note that since removelist[i] == 1, I suspect that array[4] wasn't supposed to be deleted in the first place.
During the second iteration, array[4] will be deleted again. Since this points to the already deleted object, the behaviour is undefined.
Furthermore, copies of the deleted pointer remain in the array due array[j] = array[j+1] in the inner loop, while some of the pointers will be overwritten and therefore their memory will be leaked. Simple fix to your algorithm: Delete the pointer at index first, and shift elements after deletion.
Even more: If your loop worked as you intended, first 2 iterations would have each removed an element of the array, thus reducing numofelements to 3. Then, you'd be removing an element at index 4 of an array that has valid pointers in indices 0..2. Presumably the indices to be removed must be sorted; In that case, this can be fixed by deleting the index removelist[i] - i to acount for the shifts. Another clever strategy is to remove the indices from high to low, as suggested by Paul.
Other things to consider:
The program leaks the memory allocated for array. That might not be a problem to this trivial program, but it would be a good idea to have a habit of deallocating all memory that was allocated lest you forget to do so when it matters.
It's a bad idea to use malloc unless one has specific and reasonable justification to do so. One usually doesn't have a reasonable justification to use malloc.
It's a bad idea to allocate dynamic memory at all without using a RAII container. The bugs in this program would have been trivially been avoided if std::vector had been used.
Apart from the obvious errors in memory management, the approach in general could have been made simpler if you first sorted the removelist array, and then work backwards in that array starting from the last entry going toward the first entry.
Doing this would have changed the way the array was being resized in that you would have been doing the resizing (shifting elements) on entries that will no longer be affected. In your current code, you are shifting entries, and in subsequent iterations of the loop, you need to revisit those shifted entries with a now "invalid" removelist set of indices to remove.
See mayaknife's and user2079303 answers to illustrate the issue of invalid entries after removing each item (going from lowest entry to highest entry in the removelist array). As pointed out, even a usage of std::vector would not have helped you, since this issue points out the flaw in the basic logic being used to remove the elements.
Here is how you might have addressed this in your current code if you were to work going backwards in the removelist array ( I say "might have addressed", since this is not fully tested, but it illustrates more or less the point being made):
void removeelement(int* removelist)
{
for(int i = 2; i >= 0 ; --i)
{
int index = removelist[i];
array* elementToDelete = array[index];
for(j=index; j < numofelements -2; j++)
{
array[j] = array[j+1];
}
delete [] elementToDelete;
numofelements--;
}
}
Thus on each iteration, the removelist index will still be valid, since you're going from highest entry to lowest entry in the entries to delete. Work this out on paper and you see if you reversed the way you iterated through the removelist array, you should see how this works, as opposed to going forward through the removelist array.
You also have other issues with the code, such as mixing malloc with delete[]. Doing so is undefined behavior -- never mix allocation / deallocation methods like this in a C++ program.
Having said this, here is another version of your program, but not using manual memory management:
#include <vector>
#include <algorithm>
#include <iostream>
#include <array>
struct element {
int n;
};
int main()
{
std::vector<element> arr(5);
for (int i = 0; i < 5; ++i)
arr[i].n = i;
std::array<int, 3> removelist = {1,3,4};
// sort the list
std::sort(removelist.begin(), removelist.end());
// work backwards, erasing each element
std::for_each(removelist.rbegin(), removelist.rend(),[&](int n){arr.erase(arr.begin() + n);});
// output results
for( auto& v : arr)
std::cout << v.n << '\n';
}
Live Example
Note the usage of the reverse iterators rbegin() and rend(), thus mimicking the backwards traversal of the removelist container.
This line:
delete [] array[j+1];
deletes the array of elements pointed to by 'array[j+1]'. But 'array[j+1]' was initialized by this line:
array[i] = new element;
which only allocates a single element, not an array of elements, so the deletion should only delete a single element as well. E.g:
delete array[j+1];
The main problem, however, is that the wrong elements are being deleted. To see why, let's assume that the loop which initializes 'array' assigns it pointers to five 'element' structures which we will refer to as A, B, C, D and E.
Before the call to removeelements(), 'array' contains the following pointers:
array[0] -> A
array[1] -> B
array[2] -> C
array[3] -> D
array[4] -> E
'numofelements' is 5.
Inside removeelements(), the first element to be removed is 1 and the inner loop looks like this:
for(j=1;j<3;j++){
array[j] = array[j+1];
}
This will result in the contents of 'array[2]' being copied into 'array[1]' and 'array[3]' being copied into 'array[2]. After that 'array' contains the following:
array[0] -> A
array[1] -> C
array[2] -> D
array[3] -> D
array[4] -> E
At this point 'j' contains 3 so 'delete array[j+1]' will delete the element pointed to by 'array[4]', which is 'E'.
'numofelements' is then decremented to 4.
The second element to be removed is 3. Because 'numofelements' is now 4, the inner loop will look like this:
for(j=3;j<2;j++){
array[j] = array[j+1];
}
'j' will be initialized to 3. That is greater than 2 so the body of the loop won't execute and 'array' will be left unchanged.
Since 'j' is 3 'delete array[j+1]' will again delete 'array[4]', which still points to E. So E is deleted a second time, resulting in the error that you are getting.
Were the program to continue, 'numofelements' would be decremented to 3 and we'd move on to the third element to be removed, which would be 4. This would give an inner loop like this:
for(j=4;j<1;j++){
array[j] = array[j+1];
}
'j' would be initialized to 4 and once again the body of the loop would not be executed. 'delete array[j+1]' would attempt to delete the element pointed to by 'array[5]', which is beyond the bounds of 'array' and would result in an exception.
As others have suggested, the best way to handle this is to use std::vector. However, the way your code is structured even std::vector will fail to give you the results you want because as soon as you delete one element from 'array' the indices of all of those which follow it will change, meaning that the remaining indices in 'removelist' will no longer be correct.
I suggest that whatever changes you make, you manually step through the code, as I have above, tracking the contents of the array and relevant variables so you can understand exactly what your code is doing.

Order of inserting elements into a 2D Vector in c++

I was practicing with C++ vectors, and found a problem when I was inserting elements into a 2D vector. In the following example:
#include <iostream>
#include <vector>
void fillVector(std::vector<std::vector<int> > &vec) {
std::vector<int> list1;
vec.push_back(list1);
list1.push_back(1);
list1.push_back(2);
std::vector<int> list2;
list2.push_back(3);
list2.push_back(4);
vec.push_back(list2);
return;
}
int main() {
std::vector<std::vector<int> > vect;
fillVector(vect);
std::cout << "vect size: " << vect.size() << std::endl;
for(int i = 0; i < vect.size(); i++) {
std::cout << "vect in size: " << vect.at(i).size() << std::endl;
}
}
the size of the first inner-list is 0, and the size of the second inner-list is 2. The only difference between list1 and list2 is that list1 is first inserted into the vec 2D vector, before elements are inserted into it, while elements are first inserted into list2, before it is itself inserted into the 2D vector. After returning from the function, the elements inserted into list1 are not printed, and its size remains the same.
I also attempted the first method with pointers instead,
std::vector<int> *list3 = new std::vector<int>();
vec.push_back(*list3);
list3->push_back(5);
list3->push_back(6);
However, the size of list3 when read from the calling function is still 0.
I don't understand the difference between the two approaches. Why does the list have to appended after it's elements are inserted?
It almost seems like you are expecting python-like behavior? In any case, in C++ the distinction between references, pointers, and values is very important.
Your fillVector function has the right idea, as it takes a reference to a 2D vector std::vector<std::vector<int> > &vec - notice the &. However, when you create list1, and use push_back() right away
std::vector<int> list1;
vec.push_back(list1);
you are pushing the empty vector. push_back() will create a copy of this vector, which will be contained in vect (in main), and is a completely separate vector from list1.
At this point, if you want to access the vector already pushed, you can use back(), which returns a reference to the last element in the vector vec, that is, the last one pushed.
vec.back().push_back(1);
vec.back().push_back(2);
list2 you modify before pushing back, so when the copy is made, it is made of the already modified vector. Your attempt with list3 doens't really change things much, you dereference the pointer when you push_back() and a copy is made all the same. You could make vect be std::vector<std::vector<int>*>, but I'd strongly advice against it, as you have to do manual memory management - using new.
Note: While it's important for you to learn at some point, you should really try to avoid using pointers whenever possible, specially RAW pointers (look at smart pointers instead). std::vector, and all other std containers I know of, do their own memory management - they are sure to do it more efficiently than you, and BUG FREE.
I would suggest that you simply work on the last vector pushed, as such:
void fillVector(std::vector<std::vector<int> > &vec) {
vec.push_back(std::vector<int>());
vec.back().push_back(1);
vec.back().push_back(2);
vec.push_back(std::vector<int>());
vec.back().push_back(3);
vec.back().push_back(4);
return;
}
as you can see it's pretty much the same code repeated twice, so you can easily loop to get this or other results.
vector.push_back(var) makes a copy of var and inserts it into the vector. If you use push_back() on an empty list, it copies the empty list into the vector. Changing values in the list after this does not affect the copy that was inserted into the vector. This is because you are passing an actual object to push_back(), not a pointer to an object.
In the third example, you take a step in the right direction, but you de-reference the list before you pass it in, so push_back() makes a copy of what is at that address.
A simple solution to the problem is to always set your values before you insert the list into the vector.
If you wish to be able to change the values after the list is inserted, use vect.at(i).push_back(val) to add a value to the list at i.
You could also make the vector contain pointers to other vectors, rather than the vectors themselves:
void fillVector(std::vector<std::vector<int> *> &vec) {
std::vector<int> *list1 = new std::vector<int>(); //Remember to allocate memory since we're using pointers now
list1->push_back(1);
list1->push_back(2);
vec.push_back(list1); // Copy the pointer that is list1 into vec
std::vector<int> *list2 = new std::vector<int>();
vec.push_back(list2); // Copy the pointer that is list2 into vec
list2->push_back(3);
list2->push_back(4);
return;
}
int main() {
std::vector<std::vector<int> *> vect; // Vector of pointers to vectors
fillVector(vect);
std::cout << "vect size: " << vect.size() << std::endl;
for(int i = 0; i < vect.size(); i++) {
std::cout << "vect in size: " << vect.at(i)->size() << std::endl;
}
}
std::vector<std::vector<int> *> vec = new; // Vector of pointers
When you put something into a std::vector, the vector stores a copy. Manipulating the original will have no effect on the copy. If you put a pointer into a vector of pointers the vector still stores a copy of the pointer. Both the original and the copy in the vector point at the same memory, so you can manipulate the referenced data and see a change in the referenced data.
So...
std::vector<int> list1;
vec.push_back(list1);
list1.push_back(1);
list1.push_back(2);
puts a copy of the emptylist1 into vec. Then copies of 1 and 2 are placed into the original list1. The copy of list1 in vec is unaffected.
Writing this as
std::vector<int> list1;
vec.push_back(list1);
vec.back().push_back(1);
vec.back().push_back(2);
will correct this. As will a slightly cleaner version
vec.push_back(std::vector<int>());
vec.back().push_back(1);
vec.back().push_back(2);
as it doesn't have a waste list1 hanging around cluttering up the scope.
And
vec.push_back(std::vector<int>{1,2});
will simplify even further if your compiler supports C++11 or better.
On the other hand...
std::vector<int> list2;
list2.push_back(3);
list2.push_back(4);
vec.push_back(list2);
puts copies of 3 and 4 into list2 and then puts a copy of list2, complete with copies of the copies of 3 and 4.
Similar to above,
std::vector<int> list2{3,4};
vec.push_back(list2);
can reduce the workload.
Unfortunately your experiment with list3 fails because while list3 is a pointer vec does not hold a pointer, so list3 is dereferenced and the vector referenced is copied. No pointer to the data list3 references is stored, and vec contains an empty vector for the same reason as above.
std::vector<int> *list3 = new std::vector<int>();
vec.push_back(*list3);
list3->push_back(5);
list3->push_back(6);
A few notes on storing pointers in vectors
The vector only stores a copy of the pointer. The data pointed at must be scoped in such a way that it will not be destroyed before the vector is done with it. One solution is to dynamically allocate the storage.
(This applies to dynamically allocated pointers in general) If you dynamically allocate, sooner or later someone has to clean up the mess and delete those pointers. Look into storing smart pointers rather than raw pointers and not storing pointers at all.
Familiarize yourself with the Rule of Three. vector looks after itself, but if you have two copies of the vector and you remove and delete a pointer from only one of them, you're going to have some debugging to do.

Push_back method of vector of pointers causes crash C++

I'm trying to add pointers to a vector in C++. As such:
Puzzle * puzzleStart = new Puzzle();
std::vector<Puzzle*> OPEN;
OPEN.push_back(puzzleStart);
The first time a pointer is pushed, there is no problem. The second time, it causes a crash. I'm guessing the issue is the size of the vector, but I don't understand why. Is there anything more to this?
Update: You are right, the problem is elsewhere, I just realized that it occurs while I free the vector of pointer. There is another issue, if the vector contains dupplicates of pointers I think.
if (OPEN.size()!=0){
for (int i = 0; i < OPEN.size(); ++i) {
delete OPEN[i]; // Calls ~object and deallocates *tmp[i]
}
OPEN.clear();
}
How do i make sure that it doesn't try to erase allready deleted pointers?
You are right, the problem is elsewhere, I just realized that it occurs while I free the vector of pointer. There is another issue, if the vector contains dupplicates of pointers I think.
if (OPEN.size()!=0){
for (int i = 0; i < OPEN.size(); ++i) {
delete OPEN[i]; // Calls ~object and deallocates *tmp[i]
}
OPEN.clear();
}
How do i make sure that it doesn't try to erase allready deleted pointers?
If the problem is duplication of pointers, you should consider a container that does not allow duplication, such as a set. E.g.:
std::set<Puzzle*> s;
Puzzle *puzz = new Puzzle();
auto insert_result = s.insert(puzz);
if(!insert_result.second)
{
std::cout << "\"puzz\" was a duplication. No insertion made.\n";
}
// More items inserted into s, and used, etc.
for(auto p : s)
delete p;
s.clear();
When you delete the pointer, set it to nullptr. Deleting a null pointer does not cause a crash.

How to declare an array with a variable number of elements [C++]?

How do I [dynamically?] declare an array with an 'i' number of elements?
By using a container of the standard-library instead, most commonly std::vector<>.
Other solutions contain manual allocation and deallocation of memory and are probably not worth the effort.
You could use std::vector<T>. It works like
std::vector<int> a;
a.push_back(2); // add 2 to the array
a.push_back(4);
You could go on and on, and you won't need to worry about memory allocation issues.
You have to use pointers.
Example:
float *ptrarray = new float [10];
Basically, type * pointername = new type [i];
And don't forget to clean your memory:
delete [] ptrarray;
Or it will be reserved until the program ends.
Create a Dynamically Sized Array
std::size_t N = 10;
SomeType *a = new SomeType[N];
for (auto i=0; i<N; ++i) {
std::cout << a[i] << std::endl;
}
delete [] a;
A C++ array is a pointer to a contiguous chunk of memory containing a specific type. This snippet allocates space for an array of 10 SomeType instances, constructs the array which initializes each object using the default constructor, iterates over the array, prints out each element, and then deallocates the memory by calling delete [].
Key Points:
you are required to call delete [] to deallocate the array. The array form is required to ensure that the destructor is called on each object.
after allocation, there is no way to recover the size of the array.
you can iterate over the array by index (or pointer)
This is not the way to do what you want, keep reading.
Using a vector
std::size_t N = 10;
std::vector<SomeType> v(10);
for (auto iter=v.begin(); iter!=v.end(); ++iter) {
std::cout << *iter << std::endl;
}
This snippet uses std::vector which manages a contiguous block of memory for you. Note the usage of an iterator to walk over the vector instead of using indexing. Vectors do support direct indexing so the for loop used in the previous example would work here as well.
for (auto i=0; i<v.size(); ++i) {
std::cout << v[i] << std::endl;
}
Using an iterator is the best practice and idiomatic though using a for loop may not be but I digress.
Key Points:
memory management is automatic in the case of a vector
you can append using push_back
a vector knows how many elements are in it -- call v.size() to get the number of elements
iteration is performed using the iterators returned from v.begin() and v.end()
Just use std::vector. If you need raw access to the underlying pointer, then use &v[0] or v.data() but don't do that unless you need to. Also, don't use std::auto_ptr for arrays. You will be tempted to but don't do it.