storing derived abstract classes

Started by
16 comments, last by ekrax 19 years, 8 months ago
this is sorta a follow up post, on a question i had in the beginner forum, but i think its a little off topic of that post so i decied to make a new post. anyways i decided to give the code to what i was trying to create (a way to store derived type instances in a base class array (not really an array)) ... im amazed this actually works, so many things now possible, im pretty sure though people have used this before, or maybe they havent.

#include <iostream>
#include <vector>

class base
{
	public:
		virtual void show () = 0;
};

class d1 : public base
{
		int x;
	public:
		void show ()
		{
			std::cout << x << std::endl;
		}
		d1 (int X) : x (X) {}
};

class d2 : public base
{
		double k;
	public:
		void show ()
		{
			std::cout << k << std::endl;
		}
		d2 (double K) : k (K) {}
};

class d3 : public base
{
		char c;
	public:
		void show ()
		{
			std::cout << c << std::endl;
		}
		d3 (char C) : c (C) {}
};

class container
{
		std::vector <base *> refrences;
	public:
		template <class type> void add (type object)
		{
			type *p = &object;
			refrences.push_back (p);
		}
		void execute ()
		{
			for (int n = 0; n < refrences.size (); n ++)
				refrences[n] -> show ();
		}
                  ~container ()
		{
			for (int n = 0; n < refrences.size (); n ++)
				delete refrences[n];
		}
};

int main ()
{
	container stuff;
	stuff.add <d1> (9);
	stuff.add <d2> (5.6343);
	stuff.add <d3> ('g');
	stuff.add <d2> (12.111);
	stuff.execute ();
}


well since you cant create an array, or vector, or a list of derived type objects i created a container class of my own which creates an instance of an object and then creates a pointer to it and stores it withen a vector of refrences of base class types. if the base class contains only virtual functions you can easily manipulate all dervied class types with one container. im just wondering if this is a common sort of thing to do in programs with lots of inheritance and abstract classes. also is there any potential problems with using this? thanks for any suggestions.
Advertisement
this code:

template <class type> void add (type object) {   type *p = &object   refrences.push_back (p);}


should be ringing fire bells in your head your gonna get dangling pointer syndrome.
Your add() function receives a parameter "object" which is passed by value. This means that the function gets to work on a copy of the argument, and this copy disappears at the end of the function. BUT, you put a pointer to this copy into the vector, so as soon as the function ends, the vector will contain a pointer to invalid memory.

What I like to do is to give such add functions pointers. This has two advantages:
- you are not forced to make a permanent copy for internal use (as would be the case for a pass-by-value)
- you don't need a templated function anymore, since you can pass an object of type base* to the function.

Besides that, I consider this to be a very common practice as far as polymorphism goes.

EDIT: also, the above should not compile.

stuff.add <d1>() expects an object of type d1, but you pass it 9, which is not of type d1.

But aside from your implementation errors, your method is perfectly valid.
I haven't read all the code, so bear me.

No, this is the most common way. Lots of game objects are derived from a base CGameObject class where they all replace the virtual functions. Since C++ classes have a VTable(It's a table with function pointers to the correct functions) casting down a virtual class won't remove the functions.

That's exactly how I am going to do my top-down shooter, I derive all objects from IRenderable and stop them in a list of IRenderable pointers.

However, your container can be much simpler:

class Base{    public:        virtual void Write() = 0;};class Ship : public Base{    public:        void Write() { std::cout << "I'm a ship!" << endl; }};class Car : public Base{    public:        void Write() { std::cout << "I'm a car! Vr00m Vr00m!" << endl; }};int main(){    std::vector<Base *> Objects;    Ship theShip;    Car theCar;    Objects.push_back((Base*) &theShip);    Objects.push_back((Base*) &theCar);    Car *ptr = (Car *)Objects[0]; // Get the ship    ptr->Write(); // Outputs "I'm a ship"}


It's called polymorphism.

Toolmaker

It is a fairly common thing - but it is still good to lear that kind of stuff alone.

Now, for the implementation part, you add function do no instanciate any object. It is more than dangerous - looks like your code works using some kind of magic here... Very strange...

You'll have to do :

template <class type> void add (type object){    type *p = new type(object);    refrences.push_back (p);}


or
template <class type> void add (type *object){    refrences.push_back(object);}// ...stuff.add <d1>(new d1(9));


Then you'll be free from that "dangling pointer syndrome" :)

HTH
Quote:Original post by Toolmaker
    std::vector<Base *> Objects;    Ship theShip;    Car theCar;    Objects.push_back((Base*) &theShip);    Objects.push_back((Base*) &theCar);



You don't need to cast derived types.

Quote:Original post by Toolmaker
Car *ptr = (Car *)Objects[0]; // Get the ship
ptr->Write(); // Outputs "I'm a ship"


And since Base is a polymorphic type, you can polymophically call write so there is no need for that so this is fine:

Objects[0]->Write();


Oh and thanks to who ever ranked me down for stating a fact about the problem with that "add" function of ekrax.
woah it wasnt intentional to not use new, it is by accident that i didnt use new.

this is why i used a destructor to delete everything i initialized.

Quote:
looks like your code works using some kind of magic here... Very strange...


this is magic, how the hell did this comple? im using borland command line compiler v5 mayeb they have a magic 'fix stupid programming errors' programmed into their compiler ...
Just thinking about such code as this:

template <class type> void add (type object){    type *p = new type(object);    refrences.push_back (p);}


or

template <class type> void add (type *object){    refrences.push_back(object);}// ...stuff.add <d1>(new d1(9));


Is abit redundant as you only wont to add sub-types of "base" by writing a template function your saying any related or unrelated type can be added.

This is all you need to add all sub-types of base:

void add(base& b) {   references.push_back(&b);}


or
void add(base* b) {   references.push_back(b);}
Quote:Original post by snk_kid
void add(base& b) {   references.push_back(&b);}


or
void add(base* b) {   references.push_back(b);}



Or even better use the virtual copy constructor idiom and do this:

void add(const base& b) {   references.push_back(b.clone());}
that would make more sense to do it that way, but i cant get it to work.

void add (base *object)
{
base *p = new base;
*p = object;
refrences.push_back (&p);
}

doesnt work if i use something like ...
stuff.add (d1 (9));

it wont compile, says i cant create an instance of a base class type here ... but i can use new when it is a template function.

This topic is closed to new replies.

Advertisement