Lemaru
Lemaru

Reputation: 145

How do i remove a pointer to an object in c++ from a vector dynamically?

Okay, so i'm doing this game like a project where i have 3 different objects.

void Character::shoot(){
  Shot* s = new Shot(blablabla);
  shots.push_back(s);
}

This happens dynamically, and currently i dont delete the pointers so it has to be loads of pointers to shots in that vector after a little while.

I use Bot, Character and Shot, and as i said i need help with storing and removing a pointer to the shot dynamically, preferably from a vector. I've got it to work like i put all the shot objects in a vector, but they never disappear from there. And i want to delete them permanently from my program when they collide with something, or reaches outside my screen width.

Upvotes: 2

Views: 3225

Answers (3)

Vyktor
Vyktor

Reputation: 20997

Using simple vector methods

You can iterate trough std::vector and use erase() method to remove current shot:

std::vector<cls*> shots; // Your vector of shots
std::vector<cls*>::iterator current_shot = shots.begin(); // Your shot to be deleted

while( current_shot < shots.end()){
    if((*current_shot)->needs_to_be_deleted()){
        // Remove item from vector
        delete *current_shot;
        current_shot = shots.erase(current_shot);
    } else {
        (*current_shot)->draw();
         ++current_shot; // Iterate trough vector
    }
}

erase() returns iterator to next element after removed element so while loop is used.


Setting values to null and calling std::remove()

Note that std::vector::erase() reorganize everything after delete items:

Because vectors use an array as their underlying storage, erasing elements in positions other than the vector end causes the container to relocate all the elements after the segment erased to their new positions. This is generally an inefficient operation compared to the one performed for the same operation by other kinds of sequence containers (such as list or forward_list).

This way you may end up with O(n^2) complexity so you may rather set values to null and use std::remove() as suggested by juanchopanza in comment, Erase-Remove idiom:

int deleted = 0;
for( current_shot = shots.begin(); current_shot < shots.end(); ++current_shot){
    if((*current_shot)->needs_to_be_deleted()){
        // Remove item from vector
        delete *current_shot;
        *current_shot = null;
        ++deleted;
    }
}

if( deleted){
    shots.erase( std::remove(shots.begin(), shots.end(), null));
}


Using std::list

If you need large amount of shot creations and deletions std::vector may not be the best structure for containing this kind of list (especially when you want to remove items from the middle):

Internally, vectors use a dynamically allocated array to store their elements. This array may need to be reallocated in order to grow in size when new elements are inserted, which implies allocating a new array and moving all elements to it. This is a relatively expensive task in terms of processing time, and thus, vectors do not reallocate each time an element is added to the container.

See link from Raxvan's comment for performance comparison.

You may want to use std::list instead:

List containers are implemented as doubly-linked lists; Doubly linked lists can store each of the elements they contain in different and unrelated storage locations. The ordering is kept by the association to each element of a link to the element preceding it and a link to the element following it.

Compared to other base standard sequence containers (array, vector and deque), lists perform generally better in inserting, extracting and moving elements in any position within the container for which an iterator has already been obtained, and therefore also in algorithms that make intensive use of these, like sorting algorithms.

The main drawback of lists and forward_lists compared to these other sequence containers is that they lack direct access to the elements by their position;

Which is great for removing items from the middle of the "array".

As for removing, use delete and std::list::erase():

std::list<cls*> shots;
std::list<cls*>::iterator current_shot;

// You have to use iterators, not direct access
for( current_shot = shots.begin();  current_shot != shots.end(); current_shot++){
    if( (*current_shots)->needs_to_be_deleted()){
        delete *current_shot;
        shots.erase(current_shot); // Remove element from list
    } 
}


Using std::shared_ptr

If you have more complex program structure and you use the same object on many places and you can simply determine whether you can delete object already or you need to keep it alive for a little bit more (and if you are using C++11), you can use std::shared_ptr (or use different kind of "smart pointers" which delete data where reference count reaches zero):

// Pushing items will have slightly more complicated syntax
std::list< std::shared_ptr<cls>> shots;
shots.push_back( std::shared_ptr( new cls()));
std::list< std::shared_ptr<cls>>::iterator current_shot;

// But you may skip using delete
shots.erase(current_shot); // shared_ptr will take care of freeing the memory

Upvotes: 1

Raxvan
Raxvan

Reputation: 6505

You can use std::remove and std::erase on any std container to remove content:

Shot* to_be_removed = ...;
std::vector<Shot*>::iterator i = std::remove(shots.begin(),shots.end(),to_be_removed);
std::erase(i,shots.end());
delete (to_be_removed);//don't forget to delete the pointer

This works when you know the element you want to remove. If you don't know the element you must find a way to identify the elements you want removed. Also if you have a system to identify the element it could be easier to use the container iterator in order to do the removal:

std::vector<Shot*>::iterator i = ...;//iterator the the element you want to remove
delete (*i);//delete memory
shots.erase(i);//remove it from vector

Lastly if you want to remove all pointers from the container and delete all the items at the same time you can use std::for_each

//c++ 03 standard:
void functor(Shot* s)
{
    delete(s);
}

std::for_each(shots.begin(),shots.end(),functor);
shots.clear();//empty the list

//or c++11 standard:
std::for_each(shots.begin(),shots.end(),[] (Shot * s){ delete(s); } );
//the rest is the same

Upvotes: 2

mindTree
mindTree

Reputation: 946

Simply use "delete" to deallocate the memory:

vector<Shot*>::iterator i;
for(i = shoot.begin(); i != shoot.end(); ++i)
{
    delete (*i);//delete data that was pointed
    *i = 0;
}
shoot.clear();

This will delete all elements from the heap and clear your vector.

Upvotes: 0

Related Questions