First, it is highly likely you problem is solvable by not using manual dynamic allocation in the first place. There are reasons where you can use dynamic allocation (a polymorphic storage, for example), but even then you should steer toward smart pointers rather than manual allocations.
Ditch the pointers
To do this with a regular vector of concrete objects (not pointers), you would simply
std::vector<Bullet> bulletVector;
Additions could be done with something like this:
bulletVector.emplace_back(x,y,dirX,dirY,size,duration);
bulletVector.back().assignTexture(btext);
Removing all elements based on duration reaching zero would simply use the remove/erase idiom like this:
bulletVector.erase(std::remove_if(
bulletVector.begin(),
bulletVector.end(),
[](auto const& b) { return b.getDuration() == 0; }),
bulletVector.end());
Keep in mind, while this is easily the preferred method for storing these things, it will require changes in the rest of your code. Everywhere you currently do this:
bulletVector[i]->doSomething();
will become
bulletVector[i].doSomething();
More Intelligent Pointers
If you must use dynamic allocation, don't use manually managed pointers. They're a recipe for memory leaks and ownership responsibility, and if you think it can't happen to you, you're being naive. Rather, use smart pointers. The two main smart pointer templates are std::unique_ptr
and std::shared_ptr
. There is also std::weak_ptr
, which deserve an honorable mention, as it comes in very handy in certain situations (not yours, that I can tell).
std::vector<std::shared_ptr<Bullet>> bulletVector;
Hence additions can be done like this (assuming this is done with polymorphism in mind and there may be more than one bullet type, in this case MagicBullet
and SilverBullet
, both derived from Bullet
):
std::shared_ptr<Bullet> bullet = std::make_shared<MagicBullet>(x,y,dirX,dirY,size,duration);
bullet->assignTexture(btext);
bulletVector.emplace_back(bullet);
bullet = std::make_shared<SilverBullet>(x,y,dirX,dirY,size,duration);
bullet->assignTexture(btext);
bulletVector.emplace_back(bullet);
or just store regular bullets:
bullet = std::make_shared<Bullet>(x,y,dirX,dirY,size,duration);
bullet->assignTexture(btext);
bulletVector.emplace_back(bullet);
The removal algorithm is identical, only the condition predicate function changes (and not by much)
bulletVector.erase(std::remove_if(
bulletVector.begin(),
bulletVector.end(),
[](auto const& b) { return b->getDuration() == 0; }),
bulletVector.end());
The nicest part of this is the remainder of your code will likely require little to no changes whatsoever. But I stress, that's the only real benefit (apart form the obvious, namely not having to manage the memory yourself any longer).
Given the choice between the two methods (vector of concrete objects vs vector of smart pointers) choose carefully. If the former works, use it. If it doesn't, either think about how it could work, or use the latter.
But above all, stop managing memory manually.