using the function erase in std::vector - c++

I Have this function, its purpose is to delete a pointer of class BaseFile
from a vector called children
//children is vector of type vector<BaseFile*>
void Directory::removeFile(BaseFile* file)
{
for(int i = 0 ; (unsigned)i < children.size() ; i++)
{
if ((children[i]->getName()).compare(file->getName()) == 0)
{
BaseFile* a = children[i];
children.erase(children.begin()+i);
if(a != nullptr)
{
delete a;//err in this line : double free or corruption
}
}
}
}
first question is why I AM getting an error in the line (delete a;) ?
dose the method erase removes the pointer an delete it?
if yes how can i remove the pointer from the vector without deleting it's content in Heap/Stack?

What you need to do is to use std::remove_if to obtain the vector without the matching element.
However, once you have performed the call to std::remove_if you have no way to delete the matching items as the documentation states (emphasis mine):
Removing is done by shifting (by means of move assignment) the elements in the range in such a way that the elements that are not to be removed appear in the beginning of the range. Relative order of the elements that remain is preserved and the physical size of the container is unchanged. Iterators pointing to an element between the new logical end and the physical end of the range are still dereferenceable, but the elements themselves have unspecified values (as per MoveAssignable post-condition).
Therefore we'll handle the deletion directly in the predicate. Note that we must also take care not to double free anything so we'll keep track of the deleted item through the use of an std::unordered_set
void Directory::removeFile(BaseFile *file) {
std::unordered_set<BaseFile*> deleted_set { file }; // Just to avoid deleting the input argument if it's stored in children as well...
auto match_it = std::remove_if(begin(children), end(children),
[&](BaseFile *current_file) -> bool {
bool result = current_file->getName().compare(file->getName()) == 0;
if (result && end(deleted_set) == deleted_set.find(current_file)) {
delete current_file;
deleted_set.insert(current_file);
}
return result;
});
children.erase(match_it, end(children));
}
Finally, I hope that the pointer you give as the file argument isn't a member of children as well and if it is, that you don't end up delete-ing it!
Note: Wouldn't it be possible to use smart pointers in your case? It seems that the Directory object has ownership on the BaseFile objects stored in children... So maybe std::unique_ptr would help...

Related

How can I correctly push back a series of objects in a vector in C++?

The scope of the program is to create a Container object which stores in a vector Class objects. Then I want to print, starting from a precise Class object of the vector all its predecessors.
class Class{
public:
Class(){
for (int i = 0; i < 10; ++i) {
Class c;
c.setName(i);
if (i > 0) {
c.setNext(_vec,i-1);
}
_vec.push_back(c);
}
}
};
~Class();
void setName(const int& n);
void setNext( vector<Class>& vec, const int& pos);
Class* getNext();
string getName();
void printAllNext(){ //print all next Class objects including himself
cout << _name <<endl;
if (_next != nullptr) {
(*_next).printAllNext();
}
}
private:
Class* _next;
string _name;
};
class Container{
public:
Container(){
for (int i = 0; i < 10; ++i) {
Class c;
c.setName(i);
if (i > 0) {
c.setNext(_vec,i-1);
}
_vec.push_back(c);
};
~Container();
void printFromVec(const int& n){//print all objects of _vec starting from n;
_vec[n].printAllNext();
};
private:
vector<Class> _vec;
};
int main() {
Container c;
c.printFromVec(5);
}
The problem is that all _next pointers of Class objects are undefined or random.
I think the problem is with this part of code:
class Container{
public:
Container(){
for (int i = 0; i < 10; ++i) {
Class c;
c.setName(i);
if (i > 0) {
c.setNext(_vec,i-1);
}
_vec.push_back(c);
};
Debugging I noticed that pointers of already created objects change their values.
What is the problem? How can I make it work?
Although there is really error in the code (likely wrong copypaste), the problem is really following: std::vector maintains inside dynamically allocated array of objects. It starts with certain initial size. When you push to vector, it fills entries of array. When all entries are filled but you attempt pushing more elements, vector allocates bigger chunk of memory and moves or copies (whichever you element data type supports) objects to a new memory location. That's why address of object changes.
Now some words on what to do.
Solution 1. Use std::list instead of std::vector. std::list is double linked list, and element, once added to list, will be part of list item and will not change its address, there is no reallocation.
Solution 2. Use vector of shared pointers. In this case you will need to allocate each object dynamically and put address into shared pointer object, you can do both at once by using function std::make_shared(). Then you push shared pointer to vector, and store std::weak_ptr as pointer to previous/next one.
Solution 3. If you know maximum number of elements in vector you may ever have, you can leave all as is, but do one extra thing before pushing very first time - call reserve() on vector with max number of elements as parameters. Vector will allocate array of that size and keep it until it is filled and more space needed. But since you allocated maximum possible size you expect to ever have, reallocation should never happen, and so addresses of objects will remain same.
Choose whichever solution you think fits most for your needs.
#ivan.ukr Offered a number of solutions for keeping the pointers stable. However, I believe that is the wrong problem to solve.
Why do we need stable pointers? So that Class objects can point to the previous object in a container.
Why do we need the pointers to previous? So we can iterate backwards.
That’s the real problem: iterating backwards from a point in the container. The _next pointer is an incomplete solution to the real problem which is iteration.
If you want to iterate a vector, use iterators. You can read about them on the cppreference page for std::vector. I don’t want to write the code for you but I’ll give you some hints.
To get an iterator referring to the ith element, use auto iter = _vec.begin() + i;.
To print the object that this iterator refers to, use iter->print() (you’ll have to rename printAllNext to print and have it just print this object).
To move an iterator backwards, use --iter.
To check if an iterator refers to the first element, use iter == _vec.begin().
You could improve this further by using reverse iterators but I’ll leave that up to you.

Am I deleting my vector of pointers to objects correctly?

i am currently working on a programm, where i read Information from various CAN signals and store them in 3 different vectors.
For each signal, a new pointer to an object is created an stored in two vectors.
My question is, when i am deleting the vectors, is it enough, when i delete the pointers to the objects in one vector and just clear the other one.
Here is my code:
Declaration of vectors an iterator:
std::vector <CAN_Signal*> can_signals; ///< Stores all CAN signals, found in the csv file
std::vector <CAN_Signal*> can_signals_rx; ///< Stores only the Rx CAN signals, found in the csv file
std::vector <CAN_Signal*> can_signals_tx; ///< Stores only the Tx CAN signals, found in the csv file
std::vector <CAN_Signal*>::iterator signal_iterator; ///< Iterator for iterating through the varoius vectors
Filling the vectors:
for (unsigned int i = 0; i < m_number_of_lines; ++i)
{
string s = csv_file.get_line(i);
CAN_Signal* can_signal = new CAN_Signal(s, i);
if (can_signal->read_line() == false)
return false;
can_signal->generate_data();
can_signals.push_back(can_signal);
if (get_first_character(can_signal->get_PDOName()) == 'R')
{
can_signals_rx.push_back(can_signal);
}
else if (get_first_character(can_signal->get_PDOName()) == 'T')
{
can_signals_tx.push_back(can_signal);
}
else
{
cout << "Error! Unable to detect whether signal direction is Rx or Tx!" << endl;
return false;
}
}
Deleting the vectors:
File_Output::~File_Output()
{
for (signal_iterator = can_signals.begin(); signal_iterator != can_signals.end(); ++signal_iterator)
{
delete (*signal_iterator);
}
can_signals.clear();
//for (signal_iterator = can_signals_rx.begin(); signal_iterator != can_signals_rx.end(); ++signal_iterator)
//{
// delete (*signal_iterator);
//}
can_signals_rx.clear();
//for (signal_iterator = can_signals_tx.begin(); signal_iterator != can_signals_tx.end(); ++signal_iterator)
//{
// delete (*signal_iterator);
//}
can_signals_tx.clear();
cout << "Destructor File_Output!" << endl;
}
When i uncomment the commented for-loops and run the Programm, it crashes, when the destructor is called.
So my guess is, that this is the correct way of doing so, because the pointers are allready deleted and it is enough to just clear remaining two vectors.
But i am not really sure and would really like to hear the opinion of an expert on this.
Thank you very much.
when i am deleting the vectors, is it enough, when i delete the pointers to the objects in one vector and just clear the other one.
Since the pointers in the other vectors are copies, not only is it sufficient to delete them only in the one vector, but deleting the copies would in fact have undefined behaviour. You don't ever want your program to have undefined behaviour.
Clearing any of the vectors in the destructor of File_Output seems to be unnecessary, assuming the vectors are members of File_Output. This is because the members are about to be destroyed anyway.
Am I deleting my vector of pointers to objects correctly?
With the assumption that you didn't make copies of the pointers that you delete elsewhere: Yes, that is the correct way to delete them.
Your code has a memory leak:
CAN_Signal* can_signal = new CAN_Signal(s, i);
if (can_signal->read_line() == false)
return false;
If that condition is true, then the newly allocated CAN_Signal will be leaked since the pointer is neither deleted nor stored anywhere when the function returns.
Your code is not exception safe: If any of these lines throws, then the pointer is leaked.
if (can_signal->read_line() == false)
return false;
can_signal->generate_data();
can_signals.push_back(can_signal);
It is unclear, why you would want to use explicit memory management in the first place. Unless there is a reason, I recommend that you don't do that and instead use std::vector <CAN_Signal> can_signals. This would fix your problems with memory leaks and exception safety, remove the need to implement a custom destructor, and make the implementation of copy/move constructor/assignment of File_Output simpler.
Note that if you do that, you must reserve the memory for the elements for can_signals to prevent reallocation because the pointers in other vectors would be invalidated upon reallocation. As a side-effect, this makes the program slightly faster.

Container Template Class - Decreasing Container Size

I've written some code to decrease the capacity of a templated container class. After an element is removed from the container, the erase function checks to see whether or not 25% of the total space is in use, and whether reducing the capacity by half would cause it to be less than the default size I've set. If these two return true, then the downsize function runs. However, if this happens while I'm in the middle of a const_iterator loop, I get a segfault.
edit again: I'm thinking it's because the const_iterator pointer is pointing to the old array and needs to be pointed to the new one created by downsize()...now how to go about doing that...
template <class T>
void sorted<T>::downsize(){
// Run the same process as resize, except
// in reverse (sort of).
int newCapacity = (m_capacity / 2);
T *temp_array = new T[newCapacity];
for (int i = 0; i < m_size; i++)
temp_array[i] = m_data[i];
// Frees memory, points m_data at the
// new, smaller array, sets the capacity
// to the proper (lower) value.
delete [] m_data;
m_data = temp_array;
setCap(newCapacity);
cout << "Decreased array capacity to " << newCapacity << "." << endl;
}
// Implementation of the const_iterator erase method.
template <class T>
typename sorted<T>::const_iterator sorted<T>::erase(const_iterator itr){
// This section is reused from game.cpp, a file provided in the
// Cruno project. It handles erasing the element pointed to
// by the constant iterator.
T *end = &m_data[m_capacity]; // one past the end of data
T *ptr = itr.m_current; // element to erase
// to erase element at ptr, shift elements from ptr+1 to
// the end of the array down one position
while ( ptr+1 != end ) {
*ptr = *(ptr+1);
ptr++;
}
m_size--;
// Once the element is removed, check to
// see if a size reduction of the array is
// necessary.
// Initialized some new values here to make
// sure downsize only runs when the correct
// conditions are met.
double capCheck = m_capacity;
double sizeCheck = m_size;
double usedCheck = (sizeCheck / capCheck);
int boundCheck = (m_capacity / 2);
if ((usedCheck <= ONE_FOURTH) && (boundCheck >= DEFAULT_SIZE))
downsize();
return itr;
}
// Chunk from main that erases.
int i = 0;
for (itr = x.begin(); itr != x.end(); itr++) {
if (i < 7) x.erase(itr);
i++;
}
To prevent issues with invalidated iterators during an erase loop, you can use:
x.erase(itr++);
instead of separate increment and increment calls (obviously if you're not erasing everything you loop over you'd need an else case to increment past the non-erased items.) Note this is a case where you need to use post-increment rather than pre-increment.
The whole thing looks a bit inefficient though. The way you convert to an array suggests that it's only going to work with certain container types anyway. If you expect lots of erasing from the middle of the container you might be able to avoid the memory issue by just choosing a different container; list maybe.
If you choose vector, you might find that calling shrink_to_fit is what you want.
Also note that erase has a return value that will point to the (new) location of the element after the erased one.
If you go down the line of returning an iterator to a newly created down-sized version of the container, note that comparing to the original containers end() iterator wouldn't work.
I figured out the segfault problem!
What was going on was that my x.end() was using the size of the array (NOT the capacity), where the size is the number of data elements stored in the array, instead of the capacity which is actually the size of the array. So when it was looking for the end of the array, it was seeing it as numerous elements before the actual end.
Now on to more interesting problems!

Deleting an element from an array of pointers

I'm creating a custom vector class as part of a homework assignment. What I am currently trying to do is implement a function called erase, which will take an integer as an argument, decrease my array length by 1, remove the element at the position specified by the argument, and finally shift all the elements down to fill in the gap left by "erased" element.
What I am not completely understanding, due to my lack of experience with this language, is how you can delete a single element from an array of pointers.
Currently, I have the following implemented:
void myvector::erase(int i)
{
if(i != max_size)
{
for(int x = i; x < max_size; x++)
{
vec_array[x] = vec_array[x+1];
}
vec_size --;
//delete element from vector;
}
else
//delete element from vector
}
The class declaration and constructors look like this:
template <typename T>
class myvector
{
private:
T *vec_array;
int vec_size;
int max_size;
bool is_empty;
public:
myvector::myvector(int max_size_input)
{
max_size = max_size_input;
vec_array = new T[max_size];
vec_size = 0;
}
I have tried the following:
Using delete to try and delete an element
delete vec_size[max_size];
vec_size[max_size] = NULL;
Setting the value of the element to NULL or 0
vec_size[max_size] = NULL
or
vec_size[max_size] = 0
None of which are working for me due to either operator "=" being ambiguous or specified type not being able to be cast to void *.
I'm probably missing something simple, but I just can't seem to get passed this. Any help would be much appreciated. Again, sorry for the lack of experience if this is something silly.
If your custom vector class is supposed to work like std::vector, then don't concern yourself with object destruction. If you need to erase an element, you simply copy all elements following it by one position to the left:
void myvector::erase(int i)
{
for (int x = i + 1; x < vec_size; x++) {
vec_array[x - 1] = vec_array[x];
}
vec_size--;
}
That's all the basic work your erase() function has to do.
If the elements happen to be pointers, you shouldn't care; the user of your vector class is responsible for deleting those pointers if that's needed. You cannot determine if they can actually be deleted (the pointers might point to automatic stack variables, which are not deletable.)
So, do not ever call delete on an element of your vector.
If your vector class has a clear() function, and you want to make sure the elements are destructed, simply:
delete[] vec_array;
vec_array = new T[max_size];
vec_size = 0;
And this is how std::vector works, actually. (Well, the basic logic of it; of course you can optimize a hell of a lot of stuff in a vector implementation.)
Since this is homework i wont give you a definitive solution, but here is one method of erasing a value:
loop through and find value specified in erase function
mark values position in the array
starting from that position, move all elements values to the previous element(overlapping 'erased' value)
for i starting at position, i less than size minus one, i plus plus
element equals next element
reduce size of vector by 1
see if this is a big enough hint.

Vector iterators incompatible: runtime error

I'm currently having trouble with a destructor of a class which contains a vector of objects. The application runs fine, however upon freeing the heap it throws an error.
Here is the code of my destructor:
~StaticNetwork(void) { // clear memory
for(vector<Node*>::iterator iter = nodes.begin(); iter != nodes.end(); )
nodes.erase(iter++);
}
And nodes are being added to the network as follows:
if((temp = is_already_added(regex_d[1])) >= 0) // check if the src node has already been added
{
if((temp1 = is_already_added(regex_d[2])) >= 0) // check if the next_hop has already been added
{
nodes[temp]->add_n_vchannels(regex_d[5]);
nodes[temp]->add_next_hop(nodes[temp1]);
}
else // the next_hop has not been added
{
Node *anext_hop = new Node(regex_d[2]);
nodes[temp]->add_next_hop(anext_hop);
nodes[temp]->add_n_vchannels(regex_d[5]);
nodes.push_back(anext_hop); // add next hop
param.n_of_nodes++;
}
}
The network is comprised of pointers to the actual nodes.
Any help/suggestion/reference/(constructive)criticism will be greatly appreciated.
Your iteration over the container is wrong. If the node is a member of the class, then ignore it as the destructor of the vector will take care of it. If it is not a member and you really want to remove all elements, the simplest thing is calling node.clear() (Note both are equivalent to your code, but they will leak the pointed memory if it should be managed by your class)
If the pointers are managed by your class, consider using smart pointers or specific pointer containers. Else the simplest loop to free all memory would be:
for ( std::vector<Node*>::iterator it = nodes.begin(); it != nodes.end(); ++it )
delete *it;
Note that I did not modify the container itself, just the contained elements.
You do not need to delete elements of vector manually, it will be done by vector itself. This is how destructors work: they call destructors of member objects of deleted object, so you don't have to worry about it.
erase doesn't work as you expect: it removes the elements from the container, i.e. the pointers and not the pointed object. So you are leaking memory here.
Moreover, erase invalidates the iterators following the erased element(s), thus the test iter != nodes.end(); causes the error, as you increment the pointer past it.
Anyway, you can write the code as shown by David Rodríguez - dribeas.