How to properly use a vector of pointers? - c++

I am making small simulation app, with base class named Entity and children classes named Herbivore and Carnivore. And since I am using SFML, I need to update them, deal with interactions etc, etc. My first idea was to store them in two different vectors, but that's not the most efficient solution, I guess.
Then I've tried using a little bit of polymorphism, but here is where I've utterly failed. I have one vector of pointers to the Entity objects. Here is its declaration:
std::vector<Entity*> entityVector;
While creating new objects, I call function from different class:
void ObjectFactory::createHerbivore(Sprite sprite, Vector2f position,
std::vector<Entity*> *vector)
{
Herbivore herbivore(sprite, position, m_GameResolution);
Entity* p_herbivore = &herbivore;
vector->push_back(p_herbivore);
}
Here is how I call that function in main Game class:
//Creating new herbivore objects
if (inputEvent.key.code == Keyboard::H)
{
srand((int)time(0) * dt.asMilliseconds() + 1 * 800 * entityVector.size() + 5);
herbivorePosition.x = rand() % (int)videoResolution.x;
herbivorePosition.y = rand() % (int)videoResolution.y;
factory.createHerbivore(herbivoreSprite, herbivorePosition, &entityVector);
(entityVector.back())->setDangerSprite(dangerSprite);
}
And then I proceed to work on that vector, or at least try to, but it crashes by the first try to read data saved under that pointer. Of course, through the painful and long process of debugging I've realised, that I create a new object here, pass its address and then destroy it by leaving this function. So my question is: how to deal with it, so I can keep all of this sweet polymorphism?

” I've realised, that I create a new object here, pass its address and then destroy it by leaving this function. So my question is: how to deal with it, so I can keep all of this sweet polymorphism?
You can have a vector of shared_ptr<Entity> and you can create a new dynamically allocated object with code like make_shared<Herbivore>(sprite, position, m_GameResolution).
The shared_ptr instead of a raw pointer like Entity* essentially takes care of cleanup. When there are no more shared_ptrs referring to that object, it's automatically destroyed and the memory deallocated. We say that it's owned by the collection of shared_ptrs referring to it.
The make_shared<Herbivore> instead of e.g. shared_ptr<Entity>{ new Herbivore{ sprite, position, m_GameResolution } } avoids some inefficiency (an extra dynamic allocation) of the latter, and can also help to avoid Undefined Behavior where you create two or more such objects as actual arguments in a function call.
Example:
#include <iostream>
#include <typeinfo> // for typeid
#include <memory>
#include <vector>
using namespace std;
struct Entity { virtual ~Entity(){} };
struct Herbivore: Entity {};
struct Carnivore: Entity {};
auto main() -> int
{
vector<shared_ptr<Entity>> entities;
entities.push_back( make_shared<Herbivore>() );
entities.push_back( make_shared<Herbivore>() );
entities.push_back( make_shared<Carnivore>() );
entities.push_back( make_shared<Herbivore>() );
for( auto p: entities )
{
cout << typeid( *p ).name() << endl;
}
}

Related

How do you push_back a shared_ptr variable to a vector of shared_ptrs in C++?

I’m a C++ beginner with a background in Python, Java, and JS, so I’m still learning the ropes when it comes to pointers.
I have a vector of shared pointers. Inside of a different function, I assign a shared pointer to a variable and add it to the vector. If I try to access the added element after that function exits, a segmentation fault happens:
class Bar
{
private:
std::vector<std::shared_ptr<Foo>> fooVector;
}
void Bar::addToFoo()
{
std::shared_ptr<Foo> foo (new Foo(…));
fooVector.push_back(foo);
}
void Bar::otherMethod()
{
// this method gets called sometime after addToFoo gets called
…
fooVector[anIndex]->baz(); // segfaults
…
}
But, if push_back a shared pointer and not a variable, it works.
// this works:
fooVector.push_back(std::shared_ptr<Foo>(new Foo(…)));
// this segfaults:
std::shared_ptr<Foo> foo (new Foo(…));
fooVector.push_back(foo);
I believe it happens because the foo variable gets deleted when the addToFoo function exits (correct me if I’m wrong). How do you push_back a shared_ptr variable to a vector of shared_ptrs in C++?
Why Use A Variable
Though pushing shared_ptrs to vectors directly without variables works, I prefer to use variables in order to do this:
std::shared_ptr<Rider> rider;
switch (iProcessorModesParam)
{
case PEAKS_MODE:
rider = std::shared_ptr<Rider>(new PeaksRider(…));
break;
case RMS_MODE:
rider = std::shared_ptr<Rider>(new RMSrider(…));
break;
}
volumeRiders.push_back(rider);
PeaksRider and RMSrider are subclasses of Rider. I want to store all subtypes of Rider in the same vector of Riders. I learned that adding subtypes of Rider to a vector of Riders doesn’t work and pointers are needed in order to achieve this kind of polymorphism:
std::vector<Rider> // doesn’t work with subtypes
std::vector<*Rider>
std::vector<std::shared_ptr<Rider>>
Having the std::shared_ptr<Rider> rider; variable avoids repeating the .push_back(…) code for each type of Rider.
Instead of assigning shared pointer, user reset method.
rider.reset(new PeaksRider(…));
other that this, your code snippets seems to okay to me.
segfault may have caused because of the index variable ( which may be out of range). i suggest you to use .at(index) for accessing pointer from vector and wrap that part of code in a try..catch block and see what is the real error.
And regarding...
I believe it happens because the foo variable gets deleted when the addToFoo function exits (correct me if I’m wrong).
This is not true, share_ptrs use a local counter for #of references. as soon as you pushed the pointer to vector the counter gets incremented to 2 and event after control exits the function the counter is decremented to 1. so, your object is not destroyed yet.
There is no problem on creating a shared pointer instance, storing it in a variable, and doing a push_back to a vector after that. Your code should be fine as long as the index that you use when calling "otherMethod" is valid. However, I have a couple of suggestions for your code:
When you create a shared_ptr, it is highly recommended to do it through "std::make_shared" to ensure the safety and correctness of your code in all situations. In this other post you will find a great explanation: Difference in make_shared and normal shared_ptr in C++
When accessing positions of a vector using a variable that may contain values that would cause an out-of-bounds access (which usually leads to segmentation faults) it is a good practice to place asserts before using the vector, so you will detect these undesired situations.
I just wrote a small snippet that you can test to illustrate what I just mentioned:
#include <iostream>
#include <vector>
#include <memory>
#include <cassert>
class Foo
{
public:
int data = 0;
};
class Bar
{
public:
void addNewFoo(int d)
{
std::shared_ptr<Foo> foo(new Foo());
foo->data = d;
fooVector.push_back(foo);
}
void addNewFooImproved(int d)
{
auto foo = std::make_shared<Foo>();
foo->data = d;
fooVector.push_back(foo);
}
void printFoo(int idx)
{
assert(idx < fooVector.size());
std::cout << fooVector[idx]->data << std::endl;
}
private:
std::vector<std::shared_ptr<Foo>> fooVector;
};
int main()
{
Bar b;
b.addNewFoo(10);
b.addNewFoo(12);
b.addNewFooImproved(22);
b.printFoo(1);
b.printFoo(2);
b.printFoo(0);
}

How to manage shared_ptr that points to internal data of already referenced object?

Suppose I have these classes:
struct Engine {
int engine_data;
};
struct Car {
shared_ptr<Engine> engine;
int car_data;
};
For performance reasons, I want to make them tightly packed in memory (but I don't want to lose the flexibility of the design). So, I can create a "packed" structure, and a factory that will transparently return a new B instance:
struct PackedCarAndEngine {
Engine engine;
Car car;
};
shared_ptr<Car> new_Car() {
shared_ptr<PackedCarAndEngine> packed = make_shared<PackedCarAndEngine>();
// uses aliasing shared_ptr constructor
packed->car.engine = shared_ptr<Engine>(packed, &packed->engine);
// again
shared_ptr<Car> car = shared_ptr<Car>(packed, &packed->car);
return car;
}
The problem is that this "car" instance will never be destroyed, because it have a reference count of two. When it dies, it will have a reference count of one, forever. Do you know a better way to keep using the internal shared_ptr's (so that I can attribute an "unpacked" reference if I want), and still make this packed structure?
UPDATE
I could use a no-op deleter, but then it would be very dangerous if I decide to keep the engine but not the car:
// ...
packed->car.engine = shared_ptr<Engine>(&packed->engine, do_nothing_deleter);
// ...
shared_ptr<Car> my_car = new_Car();
shared_ptr<Engine> my_engine = my_car->engine;
my_car.reset(); // Danger: engine was destroyed here!!!
cout << my_engine->engine_data; // Crash!
Consider using weak_ptr instead of shared_ptr inside struct Car, it does not contribute to reference count, but can be converted to a shared_ptr when needed.
void nop(Engine*) { /* do nothing */ }
packed->car.engine = shared_ptr<Engine>(&packed->engine, nop);
Explanation: This code creates a shared_ptr that thinks that it owns the engine but in fact it has a separate reference counter AND it does nothing when the deleter is called.

What is the problem with this piece of C++ queue implementation?

I'm trying to write a linked queue in C++, but I'm failing so far. I've created 2 files by now: my main.cpp and box.h. When trying to use my box, I receive the following message:
Description Resource Path Location Type
conversion from ‘Box*’ to
non-scalar type ‘Box’
requested main.cpp /QueueApplication line
14 C/C++ Problem
My code is as follows:
box.h
#ifndef BOX_H_
#define BOX_H_
template<class T>
class Box
{
public:
Box(T value)
{
this->value = value;
this->nextBox = NULL;
}
T getValue()
{
return this->value;
}
void setNext(Box<T> next)
{
this->nextBox = next;
}
private:
T value;
Box<T> nextBox;
};
#endif /* BOX_H_ */
main.cpp
#include<iostream>
#include "box.h"
using namespace std;
int main(int argc, char** argv)
{
Box<int> newBox = new Box<int>();
cout << "lol";
cin.get();
cin.ignore();
return 0;
}
Could you guys help me?
PS: before someone ask me why not to use stl ... I'm in a data structures class.
Removing unimportant stuff, we see you've declared a new class like this:
template<class T>
class Box
{
T value;
Box<T> nextBox;
};
How big is Box<T>?
Clearly
sizeof Box<T> >= sizeof(Box<T>::value) + sizeof(Box<T>::nextBox)
sizeof Box<T> >= sizeof(T) + sizeof(Box<T>)
0 >= sizeof (T)
uh-oh
The problem is with this line
Box<int> newBox = new Box<int>();
The new operator returns a pointer to a Box object created on the heap. The pointer will be of type Box<int>*. The left side of that expression declares a Box object. You can't directly assign a pointer-to-X to an X. You should probably just omit the new keyword unless you have a reason to want to manage the storage lifetime of the object manually. Incidentally, I'm betting you come from Java, where new is always required to create objects. Not so in C++.
Also I think it's awesome that your data structures class is introducing you to templates right off the bat.
I believe your nextBox should be a pointer.
Box<T> * nextBox;
Method setNext should deal with pointers too.
void setNext(Box<T> * next)
And newBox should be a pointer.
Box<int> * newBox = new Box<int>();
Since you come from a Java background, you are assuming that all of your objects are references. Syntax is a little different in C++.
There are multiple problems here.
First of all, in order to implement a linked list (or a queue that uses a linked list) in C++ you need to use pointers. In Java everything is a reference. C++, on the other hand, makes a clear distinction between objects and pointers to objects. (There are also references to objects, but they are irrelevant here).
Let's also forget the templates for a moment, because they are not part of the problem here.
class Box
{
int value;
Box nextBox; // wrong! should be a pointer
};
is wrong, because nextBox must be a pointer to the next element of the list/queue. The correct
way would be Box *nextBox;
By the same token setNext() should also take a pointer to Box as its argument. setNext(Box b) is an example of pass-by-value, i. e. this member function (method in Java lingo) gets its own copy of the entire Box object. This could lead to performance issues if the object is large, not to mention that any changes done to it by the function will be invisible to the caller. What you want instead here is pass-by-reference, which is accomplished by using a pointer.
The final point is that new in C++ always returns a pointer. You should have Box<int> *newBox = new Box<int>;
When you use new, you get a pointer to an object, not a plain object. Declare your variable as a pointer or just allocate your object on the stack instead.
I hope this makes sense to you, since if it doesn't, you should probably go back and read more about the basics of OOP in C++.
Guys. No raw pointers in C++ unless you really need them. Please. Especially for some poor soul who doesn't even know that operator new returns a pointer. Get a std::auto_ptr or a std::shared_ptr.

different types of objects in the same vector array?

I am using an array in a simple logic simulator program and I want to switch to using a vector to learn it but the reference I am using "OOP in C++ by Lafore" doesn't have a lot about vectors and objects so I am kinda of lost .
Here is the previous code :
gate* G[1000];
G[0] = new ANDgate() ;
G[1] = new ORgate;
//gate is a class inherited by ANDgate and ORgate classes
class gate
{
.....
......
void Run()
{ //A virtual function
}
};
class ANDgate :public gate
{.....
.......
void Run()
{
//AND version of Run
}
};
class ORgate :public gate
{.....
.......
void Run()
{
//OR version of Run
}
};
//Running the simulator using overloading concept
for(...;...;..)
{
G[i]->Run() ; //will run perfectly the right Run for the right Gate type
}
Now what I want to do is
vector(gate*) G;
ANDgate a
G.push_back(a); //Error
ORgate o
G.push_back(o); //Error
for(...;...;...)
{
G[i]->Run(); //Will this work if I corrected the error ??
}
so can a vector array hold different types of objects(ANDgate , ORgate) but they inherit the type of the vector array (gate) ????
You're half-way there:
std::vector<gate*> G;
G.push_back(new ANDgate);
G.push_back(new ORgate);
for(unsigned i=0;i<G.size();++i)
{
G[i]->Run();
}
Of course, this way you need to take care to ensure that your objects are deleted. I'd use a vector of a smart pointer type such as boost::shared_ptr to manage that for you. You could just store the address of local objects (e.g. G.push_back(&a)), but then you need to ensure that the pointers are not referenced after the local objects have been destroyed.
Yes, that will work - as long as you make run() a virtual function in gate and use the address of operator(&) on a and o as you put them in the vector.
Be careful about object lifetime issues though. If a and/or o go out of scope then your vector will contain pointers to invalid objects.
Also, the base class "Gate" should have a virtual destructor else there would be issues while cleaning up the vector and it's contents.
You are using
vector(gate*) G;
change to
vector<gate*> G;
and you should do this
G.push_back(new ANDgate());
or if you use boost use shared_ptrs as vector does quite a lot of copying and naked pointers in a vector can be fatal.

Is it possible to create a vector of pointers?

Just wondering, because of a problem I am running into, is it possible to create a vector of pointers? And if so, how? Specifically concerning using iterators and .begin() with it, ie: How would I turn this vector into a vector of pointers:
class c
{
void virtual func();
};
class sc:public c
{
void func(){cout<<"using func";}
};
sc cobj;
vector<c>cvect
cvect.push_back(cobj);
vector<c>::iterator citer
for(citer=cvect.begin();citer<cvect.end();citer++)
{
citer->func();
}
Sure.
vector<c*> cvect;
cvect.push_back(new sc);
vector<c*>::iterator citer;
for(citer=cvect.begin(); citer != cvect.end(); citer++) {
(*citer)->func();
}
Things to keep in mind:
You'll need to cleanup after your self if you use dynamically allocated memory as I did in my example
e.g.:
for(...) { delete *i; }
This can be simplified by using a vector of shared_ptrs (like boost::shared_ptr). Do not attempt to use std::auto_ptr for this, it will not work (won't even compile).
Another thing to keep in mind, you should avoid using < to compare iterators in your loop when possible, it will only work for iterators that model a random access iterator, which means you can't change out your code to use e.g. a std::list.
vector <c> cvect is not a vector of pointers. It is a vector of objects of type c. You want vector <c*> cvect. and the you probably want:
cvect.push_back( new c );
And then, given an iterator, you want something like:
(*it)->func();
Of course, it's quite probable you didn't want a vector of pointers in the first place...
Yes it is possible, and in fact it is necessary to use pointers if you intend your vector to contain objects from an entire class hierarchy rather than of a single type. (Failing to use pointers will result in the dreaded problem of object slicing -- all objects are silently converted to base class type. This is not diagnosed by the compiler, and is almost certainly not what you want.)
class c
{
void virtual func();
};
class sc:public c
{
void func(){cout<<"using func";}
};
sc cobj;
vector<c*> cvect; // Note the type is "c*"
cvect.push_back(&cobj); // Note the "&"
vector<c*>::iterator citer;
for(citer=cvect.begin();citer != cvect.end();citer++) // Use "!=" not "<"
{
(*citer)->func();
}
Note that with a vector of pointers, you need to do your own memory management, so be very careful -- if you will be using local objects (as above), they must not fall out of scope before the container does. If you use pointers to objects created with new, you'll need to delete them manually before the container is destroyed. You should absolutely consider using smart pointers in this case, such as the smart_ptr provided by Boost.
Yes, sure.
// TestCPP.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <vector>
using namespace std;
class c
{
public:
void virtual func() = 0;
};
class sc:public c
{
public:
void func(){cout<<"using func";}
};
int _tmain(int argc, _TCHAR* argv[])
{
sc cobj;
vector<c*> cvect;
cvect.push_back(&cobj);
vector<c*>::iterator citer;
for(citer=cvect.begin();citer<cvect.end();citer++)
{
(*citer)->func();
}
return 0;
}
Please note the declaration of vector<c*> cvect and the use of cvect.push_back(&cobj).
From the code provided, you are using iterator in a wrong way. To access the member an iterator is pointing to you must use *citer instead of citer alone.
You have create vector<c*> for a vector of pointers. Then use new to allocate the memory for c objects and push them into vector. Also, don't forget that you have to delete yourself and vector.clear() will not release the memory allocated for c objects. You have to store c as a vector of pointers here, otherwise the call to the virtual function will not work.
Try Boost Pointer Container Library. It has several advantages over regular vector of pointers, like:
my_container.push_back( 0 ); // throws bad_ptr
ptr_vector<X> pvec;
std::vector<X*> vec;
( *vec.begin() )->foo(); // call X::foo(), a bit clumsy
pvec.begin()->foo(); // no indirection needed