Objects of different classes in a single vector?
Solution 1
Sphere and Plane would need a common base type, or your vector would need to be composed of void*
's.
Common base type (better):
class Shape { ... };
class Sphere : public Shape { ... };
class Plane : public Shape { ... };
std::vector<Shape*> shapes;
or void*
's (not great):
std::vector<void*> shapes;
Solution 2
The classes would need to have a common base class, e.g.:
class MyBase { };
class Sphere : public MyBase { };
class Plane : public MyBase { };
Then in order to store polymorphic objects in a vector, you must store a pointer to them (because they can be different sizes from the base class). I recommend using a std::shared_ptr<MyBase>
or std::unique_ptr<MyBase>
(or use Boost if C++0x isn't available).
std::vector<std::shared_ptr<MyBase> > v;
v.push_back<std::shared_ptr<MyBase>(new Sphere());
v.push_back<std::shared_ptr<MyBase>(new Plane());
If there is no common base, you'd have to use void*
, or find a different way to do this.
Solution 3
Creating containers of polymorphic types is a classical solutions, which comes with its own problems. One of which the types have to become polymorphic just in order to add them to a container -- not a good reason. Another problem is early and tight coupling resulting in more difficult maintenance and lack of flexibility, just in order to add them to a container -- not a good reason. Fortunately, in C++ there are better alternatives.
A better solution would be storing functions and not objects themselves in containers. The common reason why you want to put different types in the same container is to perform the same actions on all of them, for example, Sphere::Draw()
or Plane::Draw()
. What you can do is create a container of draw functions instead and erase type. E.g.
vector<function<void()>> drawings;
Sphere s;
Plane p;
drawings.push_back(bind(s, &Sphere::Draw));
drawings.push_back(bind(p, &Plane::Draw));
for(auto I = drawings.begin(); I != drawings.end(); ++i) (*i)();
By doing that you avoided strong coupling and other problems of inheritance, and got a more flexible, more general solution.
The above solution works only with C++11 as it requires std::function()
Solution 4
Class Shape{...code...}
Class Sphere : public Shape{...code...}
Class Plane : public Shape{...code...}
std::vector<Shape*> List;
List.push_back(new Sphere);
List.push_back(new Plane);
or
//Base class to derived class
Shape* Shape_Sphere = new Sphere();
Shape* Shape_Plane = new Plane();
std::vector<Shape*> List;
List.push_back(Shape_Sphere);
List.push_back(Shape_Plane);
and if you want to delete the pointers
std::vector<Shape*>::iterator it;
for(it = List.begin(); it != List.end(); ++it)
{
delete *it;
}
Since the vector stores instances of Shape and Sphere/Plane are derived of the base class Shape, C++ will allow this to work
Blender
Need a freelancer? Send me an email at me@{username}so.33mail.com. You're welcome to skim my SO answers to get a general feel of what I've worked with. I have significant experience with Python and most of its popular packages and workflows (e.g. Django, Flask, SQLAlchemy, Numpy, Scipy, Scrapy, and asyncio), especially in the realms of network applications, web development, and data acquisition/processing.
Updated on June 09, 2022Comments
-
Blender almost 2 years
In my code, I have a set of objects:
class Sphere { ... class Plane { ... ...
And I need to use a collection of them (they will all have different types) in a
vector
. How would I add objects of different classes to avector
? -
Blender almost 13 yearsI did have a base
Object
class setup, but it was overriding my class-specific methods with the default ones found in the class. Here's my previous question: stackoverflow.com/questions/6274136/… -
Sven almost 13 yearsThat's a link to this question. But on a guess, I'd say the problem is that you weren't using pointers or references. You must always use pointers or references with polymorphic types.
-
Blender almost 13 yearsHere's a correct one: stackoverflow.com/questions/6271665/…
-
Blender almost 13 yearsSo if I were to reference an object from the
vector
, how would I initialize it? If I do thisObject target = *objects[i];
, I can't make use of the non-base class's functions. -
Blender almost 13 yearsWhen I declare an object like
Sphere
from thevector
, is it of the typeShape
or of the typeSphere
? I'm asking because thevector
is a scene description object which I iterate over, and the base class declaration (Shape foo = *objects[i];
) overrides the subclass's (let's say aSphere
's) internal functions with the ones I declared in the base class. It's hard for me to explain, as I don't code in C++... -
Sven almost 13 yearsYou must always use references or pointers to refer to polymorphic types. If you do
Object target = *object[i];
your object gets copied into anObject
and loses its original type. You can doObject &target = *object[i];
, orObject *target = object[i];
, which will allow you to use virtual functions. To get a particular type, useSphere *target = dynamic_cast<Sphere*>(objects[i]);
. Thedynamic_cast
will return NULL if the object was not a Sphere. -
Blender almost 13 yearsOkay, I see exactly what you mean. I tried making that change, and I get this error:
error: request for member ‘intersection’ in ‘target’, which is of non-class type ‘Object*’
. I'm trying to see what's wrong there... -
Blender almost 13 yearsI should probably go read more about this topic, as I have no idea what I'm doing... I changed my syntax from
foo.bar()
tofoo->bar()
and the error goes away, but the original problem comes back. Gah, C++, why must you be so complicated??? -
Blender almost 13 yearsWow, I have code which looks eerily similar to this. Right now, I am wrestling with the creating new objects and defining properties. My current code chunk looks like this: pastebin.com/raw.php?i=1KCMpP87
-
thatguyoverthere almost 13 yearsLet me guess that the (I'm guessing type double) radius is declared in sphere not Object? Check to make sure, because Object light is still a class Object it just happens to point to the memory of the newly allocated Sphere and can't directly access radius, Unless you didn't put radius under the reserved word "protected:" then you shouldn't be able to gain access to it until you do so.
-
Sven almost 13 yearsIs the
bar
method marked virtual on Object class? Only virtual methods will use the derived class's override when called on a variable whose type is the base class. -
Mark Rolich almost 13 yearsWhen you retrieve an element from the list, it will be typed to
Shape*
. If you only need to call non-virtual methods in your Shape class, or virtual methods first declared inShape
, then you're done. If you need to call non-virtual methods in your subclass, or access public fields in your subclass, you will have to cast the retrievedShape*
toSphere*
before use. -
Blender almost 13 yearsI'm using the arrow operator (the
->
thing) to call the functions, and it seems to kinda work. I'm getting better errors now, so thanks! -
Blender almost 13 yearsSo I'll have to define all the object-specific variables within the main
Object
class? -
Blender almost 13 yearsYes, it is
virtual
on theObject
class. -
Blender almost 13 yearsIf you'd like to see the error-causing code, I've edited it into my question. I have no idea what the problem with it is...
-
Blender almost 13 yearsActually, never mind. I replaced
Object*
withSphere*
and it all works. Thanks for your answer! -
thatguyoverthere almost 13 yearsYes, but remember to put radius under the "protected:" keyword if you want the derived class to have access to it. Just remember inheritance has its pros and cons vs. composition artima.com/designtechniques/compoinhP.html
-
Lambage over 8 yearsTree falling in the woods moment!
-
Daniel K. about 2 yearsTwo suggestions: A) bind() takes a callable object as first parameter. So it should read
drawings.push_back(bind(&Sphere::Draw, s));
B) The for-loop could be written shorter as:for(auto i: drawings) i();
. Test for yourself. What do you think?