So I'm trying to store an object in one std::vector<Derived*>, and store a pointer to that object in another std::vector<Base*>. Accessing any element of the Base* vector results in an invalid pointer. I can solve this by making the first vector also a vector of pointers (std::vector<Derived*>). However, this requires me to instantiate every single Derived object with new and I want to avoid that. Here is the code showing what I am talking about:
The problem is that if the vector of Derived objects grows, it may have to move the objects it contains which will invalidate pointers to those objects. One way around this is to use a container that doesn't have that issue, such as a std::deque.
Another option: if you know that the second container points to objects in the first container, then make the second a container of indexes into the vector instead of a a container of pointers.
So I'm trying to store an object in one std::vector<Derived>, ...
You mean std::vector<Derived*>, right?
Yep, nice catch.
The problem is that if the vector of Derived objects grows, it may have to move the objects it contains which will invalidate pointers to those objects. One way around this is to use a container that doesn't have that issue, such as a std::deque.
Ah, now I understand the issue. Thanks for the explanation.
Another option: if you know that the second container points to objects in the first container, then make the second a container of indexes into the vector instead of a a container of pointers.
That would work, but in the full scale problem, I have several std::vector<Base> containers, not just one.
Would a std::set<Base*> work just as well as std::deque
It is not you Base* container which gives you problems, it is Derived* one: you are storing ponters to Derived* container values in Base* one. As soon as you add another one entry to Derived* container, it can relocate, making all pointers to its elements invalid. You cannot avoid it by changing anything about Base* container.
It is like if you have a slip of paper with address, found out, that person lived here moved to another city and think that next time you prevent that by storing slip in your bag instead of your pocket.
You either need Derived* container to not relocate its content. Either use a container which does not reloate its content, like set, or make sure that container will not relocate (by reserving more space than you will ever need by vector). Alternatively, store pointer to vector itself and element index.
So I did some troubleshooting, and it would seem that the WorldObject pointers themselves are valid. The invalid pointer is sf::Drawable*inside of the WorldObject class. For some reason, this specific element is not being assigned correctly.
entities.push_back(Entity());
Here you are creating a temporary object that will be copied to the vector. After the copy has been made the drawable pointer will no longer be valid.
I tried using emplace_back, but that still resulted in the pointer to drawable being invalid. Am I giving the address for drawable incorrectly in my code?
Here is the runtime error:
Unhandled exception at 0x5E9CCC6E (sfml-graphics-d-2.dll) in Project for SFML Web Problems.exe: 0xC0000005: Access violation reading location 0xCCCCCCD0.
So I got it to draw on screen by making sf::RectangleShape in my derived class a pointer as well. However, a runtime error is still thrown whenever I try to delete this pointer in the destructor.
So I'm concerned about using new to allocate the above pointer. I know I need to have a delete for every new, but when I call delete then I get the following runtime error:
Unhandled exception at 0xCDCDCDCD in Project for SFML Web Problems.exe: 0xC0000005: Access violation executing location 0xCDCDCDCD.
Should I not be calling delete? Or is this not the correct fix for my problem in the first place?
If the Entity was being copied you would then get an error at compile time instead of at runtime. This makes it much easier to spot where the copies are taking place.
I don't understand why emplace_back() didn't work. Are sure you called it without any arguments? I assumed you changed to a deque because emplace_back does not solve the problems pointed out by cire.
I don't understand why emplace_back() didn't work. Are sure you called it without any arguments? I assumed you changed to a deque because emplace_back does not solve the problems pointed out by cire
Wow, now I feel stupid. I didn't know how emplace back worked. So now I can safely call delete sprite in Entity's destructor.
How would I implement emplace_back for std::deque<WorldObject*> objects;
In Holder(), I have
1 2 3 4 5
//works, because I am creating a new object. can call delete sprite;
entities.emplace_back();
//does not work, because I am trying to assign this address to a pointer. How do I
//correctly use emplace_back() with a reference to an object?
objects.emplace_back(&entities[0]);
Edit: Do I even need to delete drawable in the WorldObject destructor?
Edit edit:
I got the destructor to work as well, but only by setting drawable = nullptr in WorldObject() constructor. I've read that nullptr's are never a good sign. Is using nullptr okay here?