I am currently having some trouble with my understanding of the memory management in standard containers, especially std::vector.
It is clear to me that std::vector will resize after adding an element to it if there is not enough space reserved, therefore moving each element and changing the addresses in memory. My question is now: what happens to the element's member variables?
My problem is based on the idea that for my game engine I am currently managing scenes in a std::vector. The scenes are managed by a Scene manager class that contains a std::vector of Scenes. Adding of scenes looks like this:
std::vector<Scene> scenes;
Scene* active;
...
Scene scene;
scenes.emplace_back(scene);
active = &scenes.back();
The scenes are stack-allocated and will be thrown away after leaving the method. For exposing the currently added scene to the outside I am storing a pointer to the back of the std::vector which is the newly inserted element.
Scenes contain various members, for example an instance of the Light class. I also expose pointers to those elements to the outside for various reasons. My problem was that I tried to use those pointers in the constructor of the Scene which got constructed in the Scene manager. After adding the object to the std::vector a new object seems to be constructed even if the Scene constructor doesn't seem to be called. The "active" member now contains an different memory address than the actual Scene object I allocated before. As the vector needs to resize I am clear with that I think.
But what happens to the members of the scene? The original Scene will be destructed, the active element got another memory address. That means it points to an whole new element because of the internal resize and that new element has new members and are in my case those I want to work with.
Am I right with my understanding?
My second question includes how I should handle situations like that where I want to expose pointers to members of objects stored in a std::vector with unknown size. My current method of choice works just fine but I am not sure if it is the right way to do:
In the Scene class there is an onActivate event method which will just be called after the whole resizing and getting the inserted element of the vector has finished. When I switch the active pointer that method will be called, too. That method takes the pointers to the members of the scene and passes them around. It looks something like that:
void postConstruct() {
std::cout << "postConstruct: " << &el << std::endl;
}
And will be called at the right place in the Scene manager which currently is a friend to the Scene class because those events should not be exposed to the outer world.
active->postConstruct();
Is this the right way to go?
In case the std::vector is resized, the elements will be either moved using element's move constructor if the move constructor is declared as noexcept or it would be copied using element's copy constructor to the newly allocated position.
Whether the member pointers will be same after reallocation will depend upon how the move constructor or copy constructor is implemented for the element being inserted.
I would suggest using index than Scene* to access the elements in std::vector or use std::list if you want to use Scene*
When a vector is expanded, all iterators, pointers and references to elements become invalid. The only defined thing you can do with an invalid pointer or iterator is overwrite it with another value, and there is nothing you can do with an invalid reference. Even comparing the value to some other value makes your program ill-formed.
When you do
Scene scene;
scenes.emplace_back(scene);
active = &scenes.back();
You have two Scene objects. One is a local variable, the other is in the vector, and was copied from scene. I'm not sure that you are aware of this distinction, you probably only want one Scene object. Either all Scenes live in scenes, or you change it to be a std::vector<Scene *>, or a std::vector<std::reference_wrapper<Scene>>. If you do the latter, make sure to remove values before they are destroyed.
The language provided copy constructor will simply copy the value of each member, which in the case of pointers, will tend to be the wrong thing. You can explicitly define a copy constructor for Scene to control exactly what happens, e.g. "deep copy" pointer members.
class Copyable
{
int* ptr;
public:
Copyable() : ptr(new int) {}
~Copyable() { delete ptr; }
Copyable(const Copyable & other) : ptr(new int(*other.ptr)) {} // deep copy
Copyable& operator=(const Copyable & other)
{
*ptr = *other.ptr;
return *this;
}
};
Alternatively, you can prohibit the copying of Scenes by defining the copy constructor as = deleted
class UnCopyable
{
int* ptr;
public:
UnCopyable() : ptr(new int) {}
~UnCopyable() { delete ptr; }
UnCopyable(const UnCopyable & other) = delete;
UnCopyable& operator=(const UnCopyable & other) = delete;
};
Related
I have a vector of objects that represent GPU resources. I ended up with a pretty dangerous scenario: the objects can only be safely created or destroyed in a specific thread, but another thread still needs to be able to move them around between vectors.
As a safeguard, I deleted their copy constructor, copy assignment, and move assignment methods; the only method that isn't deleted is their move constructor.
Is there a way I can move an object like this from the middle of one vector, to the back of another vector?
class MappedBuffer {
public:
MappedBuffer(GLuint name, char * mapping) : m_name(name), m_mapping(mapping) {
assert(std::this_thread::get_id() == RENDER_THREAD_ID);
};
MappedBuffer(MappedBuffer && other) : m_name(other.m_name), m_mapping(other.m_mapping) {
other.m_name = 0;
other.m_mapping = 0;
}
MappedBuffer(const MappedBuffer & other) =delete;
MappedBuffer & operator=(MappedBuffer && other) =delete;
MappedBuffer & operator=(const MappedBuffer & other) =delete;
~MappedBuffer() {
assert(m_name == 0 || std::this_thread::get_id() == RENDER_THREAD_ID);
if (m_name) {
gl::buffer::unmap(m_name);
gl::buffer::dealloc(m_name);
}
}
private:
char * m_mapping {0}; // directly points to GPU memory
GLuint m_name {0};
};
// not the render thread
int f(size_t srcIndex, std::vector<MappedBuffer> & src, std::vector<MappedBuffer> & dest) {
// how could someone do something like the following, but without destroying any of the objects?
dest.push_back(src.begin() + srcIndex);
dest.erase(src.begin() + srcIndex);
}
Two vectors do not share an allocation (except if one is move-constructed/-assigned from the other).
Therefore moving between vectors implies creating a new object in the new vector's allocation and destroying the one in the old allocation.
Only node-based containers offer an interface to reassign elements between containers without a move of the stored object, e.g. std::list::splice.
The alternative is to add an indirection to the vector, by storing pointers (of whatever kind) to the objects instead of the objects themselves in it.
Without indirection per element this can't work.
You can't insert into the vector either under these constraints. Insertion may cause reallocation and consequently move and destruction of objects.
However, the type you are showing already contains an indirection via the char* pointer. Your class sets m_name to zero and therefore an "empty" state if it is moved-from and the destructor won't actually do anything. So destruction after a move is completely safe. The move assignment can simply behave in exactly the same way. And then there won't be any issue at all.
As long as the copy operations are deleted, the container can only destroy an object in a moved-from state or if deletion of elements is explicitly requested (e.g. .clear()). Otherwise destroying would mean dropping an object with a value that could have never had a copy made, implying that the container would lose information that it may not lose. The container also can't use a move assignment to overwrite a non-empty object for the same reason, except when erasure of an element is requested.
There are some objects that are Drawable and some that are Movable.
All movable objects are dawable.
I store all the drawable objects in a vector called drawables and movable objects in a vector called movables.
I also have vectors ships and bullets which contain objects of type Ship and Bullet respectively.
Ship and Bullet both are Movable
Here's the structure of the classes:
class Drawable {
public:
void draw();
};
class Movable : public Drawable {
public:
void move();
}
class Ship : public Movable {
public:
Ship();
}
class Bullet : public Movable {
public:
Bullet();
}
The vectors are declared as follows:
std::vector<Drawable*> drawables;
std::vector<Movable*> movables;
std::vector<Ship*> ships;
std::vector<Bullet*> bullets;
The thing is, that each time I create a Ship I have to add it in all the vectors i.e.
drawables.push_back(ship);
movables.push_back(ship);
ships.push_back(ship);
I have created separate drawables and movables vectors since I have a draw() function which calls the draw() method of all objects in the drawables vector. Similarly, I have a move() function which calls the move() method of all objects in the movables vector.
My question is, how do I change the structure to prevent adding the same thing in different vectors. I also need to remove objects from all the vectors once it's purpose is done.
For example, once the bullet hits someone or moves out of the screen, then I'll have to remove it from the vectors drawables, movables and bullets after searching it in all three vectors.
It seems like I'm not using the correct approach for storing these objects. Please suggest an alternative.
This seems more like a software engineering question than a coding question. Please migrate the question to other forum if necessary.
Assuming you are using a reasonably modern compiler, this is exactly why shared_ptr exists.
The problem is that you have no idea which vector owns the object, so you don't know which one to delete. shared_ptr takes are of this for you: it manages the lifetime of the object, and will delete it once the last reference to the object is destroyed.
To create a new Ship, you could do something like this:
auto ship = std::make_shared<Ship>();
drawables.push_back(ship);
movables.push_back(ship);
ships.push_back(ship);
At this point ship has 4 references (one for each vector, and the ship variable itself). It will automatically be deleted once it has been removed from all three vectors and the local variable goes out of scope.
If you are going to maintain a container of (pointers to) all objects of a certain type, you may want to take a RAII approach. Have the object's constructor add to the container, and the destructor remove from it. You'd also want to make sure nothing else modifies the container, so it should be a private (static) member of your class, with a public method to provide read-only access.
Even better, move this logic into its own class, so it can be re-used. This would also allow your existing containers to remain focused on what they currently do. They would just need a new data member of the helper class type.
To ease removals, I would consider using a list instead of a vector. Also, it might be worth using reference_wrapper instead of pointers. A pointer can have a null value. While you can document that the container will have no null pointers, a reference_wrapper conveys this with no additional documentation.
To get you started, here is the start of a helper class template you could use.
template <class T>
class All {
using ListType = std::list< std::reference_wrapper<T> >;
private:
static ListType the_list;
// A list's iterators are rarely invalidated. For a vector, you would
// not store an iterator but instead search when removing from the_list.
typename ListType::iterator list_it;
public:
// Read-only access to the list.
static const ListType & list() { return the_list; }
// Construction
ListAll() : list_it(the_list.end()) {} // If this constructor is needed
explicit ListAll(T & data) : list_it(the_list.insert(the_list.end(), data)) {}
// Destruction
~ListAll() { if ( list_it != the_list.end() ) the_list.erase(list_it); }
// Rule of 5
// You should also define or delete the copy constructor, move constructor,
// copy assignment, and move assignment.
// If you need the default constructor, then you probably want a method like:
//void set(T & data);
};
template <class T>
typename All<T>::ListType All<T>::the_list{};
Names are often tough to come by. I named this template based on getting something to iterate over, for example: All<Movable>::list().
I want to create a Manager that holds multiple objects and has to be used in order to actually create the objects.
The objects hold their information in a smart pointer.
this is how I implemented it:
struct Object
{
std::shared_ptr<int> number;
};
struct Manager
{
std::vector<Object> objects;
Object& createObject()
{
objects.emplace_back();
return objects.back();
}
};
int main()
{
Manager manager;
Object& object1 = manager.createObject();
object1.number = std::make_shared<int>(10);
for (Object& o : manager.objects)
{
std::cout << *o.number << std::endl;
}
}
If I execute this code I get my expected output: 10
but once I try to create multiple objects like this:
Manager manager;
Object& object1 = manager.createObject();
Object& object2 = manager.createObject();
object1.number = std::make_shared<int>(10);
object2.number = std::make_shared<int>(5);
for (Object& o : manager.objects)
{
std::cout << *o.number << std::endl;
}
I get an runtime error in the memory library at this function:
void _Decref()
{ // decrement use count
if (_MT_DECR(_Uses) == 0)
{ // destroy managed resource, decrement weak reference count
_Destroy();
_Decwref();
}
}
does anybody know why this is happening?
It is never a good idea to use vectors of class instances in conjunction with pointers or references to these class instances. Like Bo Persson already correctly answered, these pointers or references tend to become dangling due to the dynamic nature of a std::vector: when a std::vector grows, it often copies its items to a different memory position, leaving the already existing item references and pointers invalid (dangling).
You can easily avoid that by storing pointers to classes instead of the classes itself.
struct Manager
{
std::vector<std::unique_ptr<Object>> objects;
Object& createObject()
{
objects.emplace_back(std::make_unique<Object>());
return *objects.back().get();
}
};
Now std::vector may move the unique_ptr's around as it likes - the smart pointers content (raw pointers) and thus also the references never change (except if you willful change or delete them, of course)
Here's an illustration what happens when you use a vector of class instances.
The grey vertical stripes symbolize the memory - the real structure of memory and sizes are ignored here.
Step 1: You have a vector (symbolized by square brackets) holding a class instance as an item. The memory behind the vector is occupied (reality is a little different, but the image should suffice)
Step 2: You create a reference (or pointer) to your class instance. (green arrow)
Step 3: You add a second class instance to the vector. The vector has no room for its items, and thus have to move its content to another memory position. Your pointer/reference is broken! (red arrow)
And here's an illustration of the pointer solution:
Step 1: You have a vector again, but now it's a vector of smart pointers. It holds a smart pointer pointing (dark green arrow) to a class instance.
Step 2: You again create a reference (or pointer) to your class instance. (green arrow)
Step 3: You add a second pointer to a class instance to the vector. The vector has no room for its items, and thus has to move its content to another memory position. But this time only the smart pointers are moved, not the class instances itself! Class instance 1 stays at its place, smart pointer 1 still points to class instance 1, your reference stays intact, and everyone stays happy :)
Additionally: Apart from being a safe solution, using pointer vectors instead of instance vectors very often also has a performance benefit. unique_ptr are very small, nearly always much smaller than the objects they hold pointers to. And so, when std::vector has to copy its items to a different memory position, it has lot less work to do if these are only small smart pointers.
On top of that, there are some classes, which have expensive copy constructors (e.g. locking!). All of that can be avoided if the class instance is not copied at all.
When adding new elements to the vector, you risk that references to the old objects get invalidated.
See http://en.cppreference.com/w/cpp/container/vector/emplace_back where it says:
If the new size() is greater than capacity() then all iterators and references (including the past-the-end iterator) are invalidated.
So after adding object2, the reference object1 might be invalid.
I am new to SDL programming in C++ and so I'm working on a simple Invaders game.
In it I have several classes, one of which is of course the Alien class. The destructor of Alien calls for SDL_FreeSurface(SPRITEOFALIEN), in order to delete the object.
I also have a vector in the Game class, and all works well if there is only one Alien, however as soon as I spawn another Alien and I call push_back to add it to the vector, the destructor gets called, and so when it comes to actually drawing the vector of Aliens, SDL_BlitSurface crashes the program.
Sample code:
vector<Alien> aliens;
// More code.... and eventually
if (event.key.keysym.sym == SDLK_a) //Add Alien! Debugging
{
alien = new Alien(150, 0); // Alien* alien
aliens.push_back(*alien);
}
// Some more code... advancing the aliens, etc...
std::vector<Alien>::iterator it2;
for (it2 = aliens.begin(); it2 != aliens.end(); ++it2)
SDL_BlitSurface(it2->getSprite(), 0, screen, it2->getRect()); /// CRASH, FIXME
I looked up other similar questions, but they all suggested having a fixed sized vector, which I don't think is a good solution for my case, given that there should be an arbitrary number of aliens (and missiles too, I have the same problem for them).
I also know that it happens because when push_back is called, the vector is copied to a new bigger sized vector and that is why the destructors are called. Yet for some reason, the drawObjects function catches the OLD vector and crashes...
Is there a way around this?
Alien Class Definition:
#include "alien.h"
Alien::Alien(int x, int y)
{
rect.x = x; // x-pos
rect.y = y; // y-pos
sprite = SDL_LoadBMP("alien.bmp");
}
Alien::~Alien()
{
SDL_FreeSurface(sprite)
printf("Deleted Alien\n");
}
void Alien::move()
{
rect.y += SPEED;
}
Your problem is likely coming from copies of Alien. When a copy is destructed it will destroy the surface resource being shared by all of the copies. If multiple copies are being made when you call push_back() -- and I would bet that at least two copies are -- then on this line alone you double-free the SDL_Surface * and this is likely causing the crash.
I would strongly suggest defining a type specifically to manage surfaces in an RAII fashion, and then using that from Alien as you'll get the proper semantics by default.
For example:
struct SDLSurfaceDeleter
{
void operator()(SDL_Surface * p) const
{
if (p) { SDL_FreeSurface(p); }
}
};
typedef std::unique_ptr<SDL_Surface, SDLSurfaceDeleter> UniqueSDLSurface;
Now in your Alien class you have:
private:
UniqueSDLSurface sprite;
And in your constructor:
sprite = UniqueSDLSurface(SDL_LoadBMP("alien.bmp"));
(Or, better yet, initialize it in the initializer list: sprite(SDL_LoadBMP("alien.bmp")). With the initializer list approach you construct the UniqueSDLSurface object right the first time; with the assignment approach you default-construct it and then move-assign a new object. Both will work, but the initializer list approach is cleaner.)
Finally, remove the Alien destructor. Now, the Alien class should be automatically movable, but not copyable. If you get any compile-time errors about copying Alien objects you need to fix those; they were the source of your problem in the first place, and with this code the compiler simply won't let copying happen anymore, which is a good thing!
In a proper C++ standard library, std::vector will move objects when possible during a reallocation, so now you'll be able to use it properly because it will be moving your Alien objects to their new destination instead of copying them.
As a side note, you could similarly use std::shared_ptr to create a shared surface, where the surface is freed when the last std::shared_ptr is destroyed. This might make more sense here since all Alien objects use the same source bitmap; you could load it once and then share it among all of the instances:
std::shared_ptr<SDL_Surface> make_shared_surface(SDL_Surface * surface)
{
return std::shared_ptr<SDL_Surface>(surface, SDLSurfaceDeleter());
}
Further, here you leak memory, because you don't delete the object you allocated with new:
alien = new Alien(150, 0); // Alien* alien
aliens.push_back(*alien);
But you don't need to do this anyway, since you can do:
aliens.push_back(Alien(150, 0));
Or, better yet:
aliens.emplace_back(150, 0);
The essential issue with a vector of varying size is that when the size increases, the vector may have to reallocate and put all its contents in a new location. This will copy all the existing contents and then destroy the old objects.
Since you don't want your object destroyed (that cleans up the SDL surface), we need to arrange that the vector copies and destroys something else. It could be a raw pointer, but a much better approach is to use a smart pointer such as std::unique_ptr.
Try this:
std::vector<std::unique_ptr<Alien>> aliens;
...
if (event.key.keysym.sym == SDLK_a) //Add Alien! Debugging
{
alien = new Alien(150, 0); // Alien* alien
aliens.emplace_back(alien);
}
Then when you use it, you'll need to dereference it...
for (std::unique_ptr<Alien>& that_alien : aliens)
SDL_BlitSurface(that_alien->getSprite(), 0, screen, that_alien->getRect());
This avoids ever using the broken Alien copy constructor. You should go one step further and disable that to make sure it is never called by accident. In your class definition, add:
class Alien
{
// add one of the two lines below
private: Alien(const Alien&) = delete; // if your compiler has = delete implemented
private: Alien(const Alien&); // this is almost as good
};
First of all the fact that you have issue when your objects are copied points that most probably you broke the "rule of three" -http://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)
if your objects cannot be copied you should prohibit copy and/or move (making copy/move ctor private for example) at least then compiler will tell you if there is a problem and you try to copy them.
Now to avoid copying your objects in a vector you need to reserve enough space in vector and call emplace upon creation if you can use C++11. Or you should keep objects by pointers. Your code where you create instance dynamically suggest you or somebody else planned to do so, but for whatever reason put it into vector<Alien> by value. Also you should not keep objects by raw pointers in the vector (unless you experienced enough and know what you are doing) so you should use smart pointer, either standard if you use C++11 or from boost or similar library otherwise:
typedef std::shared_ptr<Alien> AlienPtr;
typedef std::vector<AlientPtr> Aliens;
Aliens aliens;
if (event.key.keysym.sym == SDLK_a) //Add Alien! Debugging
{
aliens.push_back( std::make_shared<Alien>( 150, 0 ) );
}
Most probably the Alien class allocates some pointers using new, frees them upon destruction and does not have a copy constructor. In that case, the default implementation is to copy the pointers, not to allocate new copies of the objects. You should write a copy constructor for Alien that does precisely that. If you imagine that member is the only member of Alien (of class Member), that would look like that:
Alien(const Alien & a) {
member = new Member(*member);
}
Of course, if in turn Member allocates some objects, you should make the right copy constructor there too.
I'm writing a content management system to avoid duplicates of the same loaded texture in my game engine. The following is the function for retrieving content from the previously loaded in objects or to load in a new object if none is available.
template <class T>
T* GetContent(const char* path) {
// Check if it already exists, if yes return it
for (ContentEntry& entry : m_ContentList) {
// Same Type?
if (strcmp(entry.Type, T::GetType()) == 0)
// Same Path?
if (strcmp(entry.C->GetPath(), path) == 0)
return (T*)entry.C;
}
// Since it doesn't exist, create it
ContentEntry contentEntry (
T::GetType(),
(Content*)new T(path));
// Add it to the list
m_ContentList.push_back(contentEntry);
// And Return it
return (T*)contentEntry.C;
}
And this is the struct used to store content entries and the vector they're stored in.
struct ContentEntry {
const char* Type;
Content* C;
ContentEntry(const char* type, Content* c) :
Type(type),
C(c)
{ }
~ContentEntry() {
delete C;
}
};
std::vector<ContentEntry> m_ContentList;
Whenever this function tries to return the value, the app crashes. When I change contentEntry to a pointer (updating the code around it appropriately) it returns with no problem but I have to change the entire vector to point to ContentEntry pointers and then manually delete them which I would like to avoid if possible. How could I make this function work correctly?
Additionally, when using the pointer and stepping through the foreach loop, the vector seems to grow drastically for no clear reason, how can I stop this from happening?
Edit: For now fixed the crashing problem which I'll later refine, but the vector growing out of control is still there.
Edit2: The vector growing seems to just disappear after exiting the function so I'm just gonna mark something as answer.
From the looks of it, you are deleting something which looks like a string using delete rather than delete[]. Of course, this assumes that the string was allocated in the first place. Based on your comment you try to delete a string literal which causes undefined behavior at that point.
That said, please note that you are slicing your object when you insert it into the vector and, more importantly, you don't get a deep copy of the ContentEntry members (this type is lacking a copy constructor and probably an assignment operator). Thus, after inserting the ContentEntry into your std::vector<ContentEntry> the newly allocated object is gone. Another interesting bit is that you cast your T* to a Content*. The allocate object is deleted through a pointer to Content. Thus, either the cast is unnecessary (and hopefully your type Content has a virtual destructor) or things will start going bad at that point.
// Since it doesn't exist, create it
ContentEntry contentEntry (
T::GetType(),
(Content*)new T(path));
// Add it to the list
m_ContentList.push_back(contentEntry);
// And Return it
return (T*)contentEntry.C;
Your problem is in these three lines, in combination with ContentEntry not having a copy constructor (and copy assignment).
in the first part you create a ContentEntry instance
then you push_back a copy of that instance. this copy will point to the same T instance that the original ContentEntry pointed to.
finally, the function exits, return the pointer-to-T. But at the exit, your local copy contentEntry is destroyed, which will delete the T-instance that the returned pointer points to.
In essence, you are not following the Rule of Three and are being punished for it.
You need a copy constructor for ContentEntry.