Problems when looping through a list of pointers - C++ - c++

I have a list of pointers to a base abstract class (Entity)
std::list<Entity*> m_entities;
I have created a typedef for iterating through this class
typedef std::list<Entity*>::const_iterator entityIter;
I then try and iterate through each pointer in the list
for (entityIter i = m_entities.begin(); i != m_entities.end(); ++i)
{
const Entity &e = *i; // ERROR
e.DoStuff();
}
I get the following error when attempting to reference each pointer
IntelliSense: no suitable constructor exists to convert from "Entity *const" to "Entity"
What have I done incorrectly?
EDIT:
I have tried to use std::shared_ptr
std::list<std::shared_ptr<Entity>> m_entities;
I can't add to the list this way though
Entity::Entity(Game *game)
: m_game(game)
{
m_game->g_idGenerator->generateNewID();
m_game->m_entities.push_back(this); // ERROR
}
Using the following
m_game->m_entities.push_back(std::shared_ptr<Entity>(this));
gives me this error
error C2664: 'void std::list<_Ty>::push_back(_Ty &&)' : cannot convert parameter 1 from >'Entity' to 'std::tr1::shared_ptr<_Ty> &&'
EDIT 2:
Current code summary
for (entityIter i = m_entities.begin(); i != m_entities.end(); ++i)
{
// *i dereferences the iterator and returns an Entity*
// **i would additionally deference the pointer
// Adding 'const' infront of Entity means that I can't alter the Entity
Entity &e = **i;
e.draw(dt); // Causes access violation error with standard pointers
}
Have tried converting to std:shared_ptr to see if it would avoid the error triggered by the code above.
However, I am now having trouble adding the Entity to the list of std::shared_ptr
m_game->m_entities.push_back(std::shared_ptr<Entity>(this));
So in summary I have the access violation error with a standard pointer and I can't add to the list with a shared_ptr.
Populating the list is done via the constructor of the base Entity class
Entity::Entity(Game *game)
: m_game(game)
{
m_game->g_idGenerator->generateNewID();
// shared_ptr version
m_game->m_entities.push_back(std::shared_ptr<Entity>(this)); // ERROR C2664
// raw pointer version
//m_game->m_entities.push_back(this); // ACCESS VIOLATION ERROR when calling methods
}

const Entity &e = *i;
*i dereferences the iterator and returns an Entity*.
**i would additionally deference the pointer.
To avoid keeping the reference around, you can use (*i)->memberFunction(...);
Don't forget that if you allocated the Entity's with operator new that you also need to operator delete them.
Here is an example using std::shared_ptr since your code is looking to complex to discuss on a page.
I created a trivial Entity class and used it in std::shared_ptr. I put the same std::shared_ptr<Entity> multiple times purely for demonstration that std::shared_ptr manages this information including copying itself into the list.
#include <memory>
#include <iostream>
#include <string>
#include <list>
using namespace std;
class Entity {
private:
string name;
public:
Entity(const std::string& n) :
name(n)
{ }
const string& getName() {
return name;
}
};
int main(int argc, char** argv) {
list<shared_ptr<Entity> > l;
shared_ptr<Entity> sp(new Entity("Repeated!"));
l.push_back(sp);
l.push_back(sp);
l.push_back(shared_ptr<Entity>(new Entity("Foo")));
l.push_back(sp);
l.push_back(shared_ptr<Entity>(new Entity("Bar")));
l.push_back(sp);
for(list<shared_ptr<Entity> >::const_iterator iter = l.begin();
iter != l.end(); ++iter)
{
cout << ">> " << (*iter)->getName() << endl;
}
};
Note: There is a difference between you putting the exact same raw pointer into multiple std::shared_ptr objects and copying a std::shared_ptr as push_back does. The latter case is fully managed by std::shared_ptr while the former case has each std::shared_ptr attempting to manage independently.

Option A) You wanted to access the elements and gurantee you do not change them:
typedef std::list<Entity*>::const_iterator entityIter;
std::list<Entity*> m_entities;
for (entityIter i = m_entities.begin(); i != m_entities.end(); ++i)
{
const Entity &e = **i; // ERROR
e.DoStuff();
}
Option B) You wanted to access the elements and change them:
typedef std::list<Entity*>::iterator entityIter; // no const_
std::list<Entity*> m_entities;
for (entityIter i = m_entities.begin(); i != m_entities.end(); ++i)
{
Entity &e = **i; // ERROR
e.DoStuff();
}

Related

Pointer to a vector doesn't point

I have simplified the code as much as possible.
So I have two class:
class EntityManager
{
public:
std::shared_ptr<std::vector<Entity> > getEntities()
{
return std::make_shared<std::vector<Entity> >(m_entities);
}
private:
std::vector<Entity> m_entities{};
};
and
class System
{
public:
void loadEntities(std::shared_ptr<std::vector<Entity> > entities)
{
m_entities = entities;
}
private:
std::shared_ptr<std::vector<Entity> > m_entities;
};
Now basically I want the m_entities of System to point to the m_entities of EntityManager.
I did this:
system = System();
system.loadEntities(m_entityManager.getEntities());
But then I pushed back an element into the m_entities vector of EntityManager and this element wasn't added in the m_entities vector of System, which means my pointer doesn't point.
Where is my mistake?
Thanks!
Your problem is this line: return std::make_shared<std::vector<Entity> >(m_entities);
What is happening is that the shared_ptr manages a new std::vectory<Entity> container which is initialized as a copy of m_entities. Therefore, modifying the instance in the shared_ptr doesn't modify the data member in the EntityManager class and of course the shared_ptr won't see changes made to EntityManager::m_entities.
std::make_shared doesn't "make this thing shared"; it "makes a thing that will be shared".
So, you can't just make a shared pointer out of nowhere that points to something that already exists.
Your code dynamically allocates a std::vector, copy constructed from m_entities and managed by a std::shared_ptr. It's shorthand for this:
std::vector<Entity>* ptr_to_copy = new std::vector<Entity>(m_entities);
return std::shared_ptr(ptr_to_copy);
It's not clear what you're trying to do, from the code that (by your own admission) does not achieve that goal. But it seems unlikely that std::shared_ptr is appropriate here.
If it is, then make the vector dynamically-allocated and shared from the start; otherwise, just return a reference to the vector as it is.
Hack example of a pointer-free solution.
#include <string>
#include <iostream>
#include <vector>
//Hack-sample Entity class
class Entity
{
public:
Entity(const char * name): m_name(name)
{
}
void print() // this is stupid in real life. Prefer a << overload
{
std::cout << "Hi! I'm " << m_name << "!\n";
}
private:
std::string m_name;
};
class EntityManager
{
private:
std::vector<Entity> m_entities;
public:
// hide the fact that a vector is being used to store the entities.
// you can now swap out the vector for most standard containers without
// changing any code other than the using and the declaration of m_entities
using iterator = std::vector<Entity>::iterator;
EntityManager(): m_entities({"bob", "bill"})
// just pre-loading a few test entities
{
// RAII says you should load the entities from their source here
}
// get the first entity.
iterator begin()
{
return m_entities.begin();
}
// get the end of the entity list
iterator end()
{
return m_entities.end();
}
// adds an entity
void addEntity(const Entity & entity)
{
m_entities.push_back(entity);
}
// removes an entity
iterator removeEntity(iterator rem)
{
return m_entities.erase(rem);
}
};
class System
{
public:
// example method to show System working with EntityManager by printing all of the Entities
void printEntities()
{
for (EntityManager::iterator it = m_entityManager.begin();
it != m_entityManager.end();
++it)
{
it->print();
}
}
// example method to show System working with EntityManager by adding Entities
void addMoreEntities()
{
m_entityManager.addEntity(Entity("Ted \"Theodore\" Logan"));
m_entityManager.addEntity(Entity("Excellent!!!"));
}
private:
EntityManager m_entityManager ;
};
// sample test
int main()
{
System test;
test.printEntities();
test.addMoreEntities();
test.printEntities();
}
THIS HAS BEEN A HACK. THIS HAS ONLY BEEN A HACK.
If you want to do EntityManager right, see Writing your own STL Container for hints. If you want all of the bells and whistles, the job is fairly complicated. Depending on how you are using EntityManager and the complexity of the Entity management logic, you may be better off discarding EntityManager and just using the plain, old std::vector.
Addendum: What is meant by Resource Acquisition is Initialization (RAII)?

C++, Objects on vector

I wanna store objects on vectors. But I do not know why it does not work.
‪#‎include‬ <iostream>
#include <vector>
using namespace std;
I have a Persona class in the Persona.h file. And it only has two method:
The default constructor and a method called mensaje(), both are public and it does not have any private member.
#include "Persona.h"
int main()
{
vector<Persona> personas;
Persona persona1;
Persona persona2;
personas.push_back(persona1);
personas.push_back(persona2);
vector<Persona>::const_iterator p;
for(p = personas.begin(); p <= personas.end(); p++) {
Here is where I get the error message
p.mensaje();
}
}
I think problem is the way that I am trying to call 'p'.
Is right that I try to use const_iterator instead of any other type?
p is iterator not object itself, you need to dereference it:
(*p).mensaje();
OR
p->mensaje();
And
update:
for(p = personas.begin(); p <= personas.end(); p++) {
to:
for(p = personas.begin(); p != personas.end(); p++) {
^^^^^^
You are trying to call a non-const method on a const object (the object referenced by a const iterator). Since the mensaje() method does not modify the object, it should be declared const, like so:
void Persona::mensaje() const;
After you make this change, you should be able to call the method on the const object (reference) returned from the const iterator.
(...in addition to the other syntax errors mentioned in other answers.)

Pointer to Element in Vector

I am farily new to c++ and have already read some topics about storing pointers to objects or the objects themselves in a vector.
I decided to store the objects in the vector, because I do not push_back many objects at runtime, the vector is just created once and leaved like this.
My problem now is, that I have another object that gets a vector as argument and searches for a certain object in the passed vector. If it finds this object, it stores a pointer to it, if not, the variable is set to NULL.
Eventhough I do not push_back any items, the pointer seems to point to a wrong location in other functions.
The object that searches for the element in the vector has a public function in which the pointer should be returned. It would be very slow if I search for the object at every function call, so this should not be an option.
Are there other solutions or do I have to switch to a vector of pointers?
Some code snippets:
Constructor of the object that searches the vector:
MySearch::MySearch(QVector<Obj> objVector)
:objVector(objVector) {
found = NULL
foreach(Obj o, this->objVector) {
if(..found..) {
found = &o;
break;
}
}
}
Getter function:
Obj* MySearch::getObject() {
return found;
}
The problem is because the variable o is local and will be out of scope as soon as the loop ends. If you take the address of the vector element instead of the o, it will works.
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class MySearch
{
public:
MySearch(const vector<string> &items)
: items_(items)
{
// Skipping validation
found_ = &(items_[5]);
}
string *getObject() {return found_;}
private:
vector<string> items_;
string *found_;
};
int main()
{
string str = "test#";
vector<string> aux;
for (int i = 0; i < 10; ++i)
aux.push_back(str + (char)('0' + i)); // test#0 ... test#9
MySearch ms(aux);
cout << *(ms.getObject()) << endl; // test#5
return 0;
}
foreach(Obj o, this->objVector) {
if(..found..) {
found = &o;
break;
}
} // life time of o ends here.
o resides on stack and it's life-time is limited to the loop only. Having reference to it and later returning causes undefined behavior.
If you were to use BOOST_FOREACH (from the boost C++ libraries), then you could use a non-const reference to the objects in the vector. Q_FOREACH does not support non-const references:
BOOST_FOREACH(Obj& o, this->objVector) {
if(..found..) {
found = &o;
break;
}
}
Alternatively use iterators and a for loop.

Get Element Position within std::vector

How do I get the position of an element inside a vector, where the elements are classes. Is there a way of doing this?
Example code:
class Object
{
public:
void Destroy()
{
// run some code to get remove self from vector
}
}
In main.cpp:
std::vector<Object> objects;
objects.push_back( <some instances of Object> );
// Some more code pushing back some more stuff
int n = 20;
objects.at(n).Destroy(); // Assuming I pushed back 20 items or more
So I guess I want to be able to write a method or something which is a member of the class which will return the location of itself inside the vector... Is this possible?
EDIT:
Due to confusion, I should explain better.
void Destroy(std::vector<Object>& container){
container.erase( ?...? );
}
The problem is, how can I find the number to do the erasing...? Apparently this isn't possible... I thought it might not be...
You can use std::find to find elements in vector (providing you implement a comparison operator (==) for Object. However, 2 big concerns:
If you need to find elements in a container then you will ger much better performance with using an ordered container such as std::map or std::set (find operations in O(log(N)) vs O(N)
Object should not be the one responsible of removing itself from the container. Object shouldn't know or be concerned with where it is, as that breaks encapsulation. Instead, the owner of the container should concern itself ith such tasks.
The object can erase itself thusly:
void Destroy(std::vector<Object>& container);
{
container.erase(container.begin() + (this - &container[0]));
}
This will work as you expect, but it strikes me as exceptionally bad design. Members should not have knowledge of their containers. They should exist (from their own perspective) in an unidentifiable limbo. Creation and destruction should be left to their creator.
Objects in a vector don't automatically know where they are in the vector.
You could supply each object with that information, but much easier: remove the object from the vector. Its destructor is then run automatically.
Then the objects can be used also in other containers.
Example:
#include <algorithm>
#include <iostream>
#include <vector>
class object_t
{
private:
int id_;
public:
int id() const { return id_; }
~object_t() {}
explicit object_t( int const id ): id_( id ) {}
};
int main()
{
using namespace std;
vector<object_t> objects;
for( int i = 0; i <= 33; ++i )
{
objects.emplace_back( i );
}
int const n = 20;
objects.erase( objects.begin() + n );
for( auto const& o : objects )
{
cout << o.id() << ' ';
}
cout << endl;
}
If you need to destroy the n'th item in a vector then the easiest way is to get an iterator from the beginning using std::begin() and call std::advance() to advance how ever many places you want, so something like:
std::vector<Object> objects;
const size_t n = 20;
auto erase_iter = std::advance(std::begin(objects), n);
objects.erase(erase_iter);
If you want to find the index of an item in a vector then use std::find to get the iterator and call std::distance from the beginning.
So something like:
Object object_to_find;
std::vector<Object> objects;
auto object_iter = std::find(std::begin(objects), std::end(objects), object_to_find);
const size_t n = std::distance(std::begin(objects), object_iter);
This does mean that you need to implement an equality operator for your object. Or you could try something like:
auto object_iter = std::find(std::begin(objects), std::end(objects),
[&object_to_find](const Object& object) -> bool { return &object_to_find == &object; });
Although for this to work the object_to_find needs to be the one from the actual list as it is just comparing addresses.

Object slicing and implicit type conversion

Here's a simplified version of the code I'm using:
namespace BasketNovel {
void Engine::BuryEntities()
{
std::list<Entity*>::iterator iter = p_entities.begin();
while (iter != p_entities.end())
{
if ( (*iter)->getAlive() == false )
{
delete (*iter);
iter = p_entities.erase( iter ); //.erase returns next element
}
else iter++;
}
}
}
I'm getting the following warning from Intel Static Analysis:
BasketNovel.cpp(567): warning #12221: slicing of object "iter" passed as actual argument 2 in call to "std::_List_iterator > > std::list >::erase(std::_List_const_iterator > >)" occurs due to implicit type conversion
I believe that this is basically saying that I'm causing an implicit type conversion in:
iter = p_entities.erase( iter );
(note: I get the same warning even if I change my code to: p_entities.erase( iter++ ); )
I don't quite understand what I'm "slicing" in the above.
What exactly does this mean and how I should go about solving this warning? I'd rather slightly convoluted code than turning off warning messages completely.
What is Object Slicing
Object Slicing is the fact of copying/moving only part of an object, this occurs in general with Base/Derived couples:
struct Base { int i; };
struct Derived: Base { int j; };
void slice() {
Derived d = {};
Base b(d); // b is a "sliced" version of `d`
}
and can lead to nastiness.
Here though, this is just a false positive...
Can it be easier ?
Yes, certainly.
// Place to be deleted values at the end
auto const it = std::partition(p_entities.begin(), p_entities.end(),
[](Entity const* e) { return not e or not e->getAlive(); });
// Delete them
std::for_each(it, p_entities.end(), [](Entity const* e) { delete e; });
// Remove them
p_entities.erase(it, p_entities.end());
It looks like your std::list::erase() method is expecting a std::list<Entity*>::const_iterator and you are passing it an std::list<Entity*>::iterator. This could mean you are compiling the code with C++11 support.
One solution would be to perform the removals in two steps. First, use std::for_each to delete and set to 0 pointers to objects that are not alive.
#include <algorithm>
void deleteDead(Entity* e) {
if (e->getAlive()) return;
delete e;
e = 0;
}
std::for_each(p_entities.begin(), p_entities.end(), deleteDead);
Second, use the [erase-remove idiom](erase-remove idiom to remove elements that are 0.
#include <algorithm>
p_entities.erase(std::remove(p_entities.begin(), p_entities.end(), 0),
p_entities.end() );
After about a month of doing other work, I've realised the answer to the problem was basically in changing
std::list::iterator
to
std::list::const_iterator
The slicing was occurring because .erase() required a const_iterator and made the implicit conversion from iterator.
I'd recommend typedef ing std::list in the header to cover possible future type changes.
I'm keeping MatthieuM.'s answer up though because I think the definition on Object Slicing is far more useful than this answer itself.