I am seeking to improve my C++ skills by writing a sample software renderer. It takes objects consisting of points in a 3d space and maps them to a 2d viewport and draws circles of varying size for each point in view. Which is better:
class World{ vector<ObjectBaseClass> object_list; public: void generate(){ object_list.clear(); object_list.push_back(DerivedClass1()); object_list.push_back(DerivedClass2());
or...
class World{ vector<ObjectBaseClass*> object_list; public: void generate(){ object_list.clear(); object_list.push_back(new DerivedClass1()); object_list.push_back(new DerivedClass2());
?? Would be using pointers in the 2nd example to create new objects defeat the point of using vectors, because vectors automatically call the DerivedClass destructors in the first example but not in the 2nd? Are pointers to new objects necessary when using vectors because they handle memory management themselves as long as you use their access methods? Now let's say I have another method in world:
void drawfrom(Viewport& view){ for (unsigned int i=0;i<object_list.size();++i){ object_list.at(i).draw(view); } }
When called this will run the draw method for every object in the world list. Let's say I want derived classes to be able to have their own versions of draw(). Would the list need to be of pointers then in order to use the method selector (->) ?
Having vector of objects is much slower than a vector of pointers. The results are because algorithms such as sorting need to move elements inside the container. So they not only read the data but also perform a copy (when the algorithm decides to swap items or move to a correct place according to the order).
The pointers pointing to objects are referred to as object pointers. There is a relationship between pointers and objects. Using pointers all the members of the class can be accessed. The following example shows various operations that can be performed on the members of the class through a pointer to objects.
You can store pointers in a vector just like you would anything else. Declare a vector of pointers like this: vector<MyClass*> vec; The important thing to remember is that a vector stores values without regard for what those values represent.
An ordinary vector encountered in C++ programming, is a vector of objects of the same type. These objects can be fundamental objects or objects instantiated from a class. This article illustrates examples of vector of pointers, to same object type.
Since you are explicitly stating you want to improve your C++, I am going to recommend you start using Boost. This can help you with your problem in three different ways:
shared_ptr
Using a shared_ptr
could declare your vector like this:
std::vector< boost::shared_ptr< ObjectBase > > object_list;
And use it like this:
typedef std::vector< boost::shared_ptr< ObjectBase > >::iterator ObjectIterator; for ( ObjectIterator it = object_list.begin(); it != object_list.end(); it++ ) (*it)->draw(view);
This would give you polymorphism and would be used just like it was a normal vector of pointers, but the shared_ptr
would do the memory-management for you, destroying the object when the last shared_ptr
referencing it is destroyed.
Note about C++11: In C++11 shared_ptr
became part of the standard as std::shared_ptr
, so Boost is no longer required for this approach. However, unless you really need shared ownership, it is recommended you use std::unique_ptr
, which was newly introduced in C++11.
ptr_vector
Using a ptr_vector
you would do it like this:
boost::ptr_vector< ObjectBase > object_list;
And use it like this:
typedef boost::ptr_vector< ObjectBase >::iterator ObjectIterator; for ( ObjectIterator it = object_list.begin(); it != object_list.end(); it++ ) (*it)->draw(view);
This would again be used like a normal vector of pointers, but this time the ptr_vector
manages the lifetime of your objects. The difference to the first approach is, that here your objects get destroyed when the vector gets destroyed, whereas above they may live longer than the container, if other shared_ptr
s referencing them exist.
reference_wrapper
Using a reference_wrapper
you would declare it like this:
std::vector< boost::reference_wrapper< ObjectBase > > object_list;
And then use it like this:
typedef std::vector< boost::reference_wrapper< ObjectBase > >::iterator ObjectIterator; for ( ObjectIterator it = object_list.begin(); it != object_list.end(); it++ ) it->draw(view);
Notice that you do not have to dereference the iterator first as in the above approaches. This does however only work if the lifetime of your objects is managed elsewhere and is guaranteed to be longer than that of the vector
.
Note about C++11: reference_wrapper
has also been standardized in C++11 and is now usable as std::reference_wrapper
without Boost.
As pointed out in Maciej Hs answer, your first approach results in object slicing. In general you may want to look into iterators when using containers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With