Sign in to follow this  
Vero

Polymorphism and pointer arrays in c++

Recommended Posts

So, I haven't been able to find a good explanation on this.

I want a dynamic size to my array, basically then I read in the level it tells me how many objects I need so I do the following:

MObject *objects = new MObject[num_of_obj];

after I find out how many I need through the text file I created that's reading in the level, I find out what KIND of MObjects they are so one of them is a SpeedBoost obj (inherits MOBject)

*(object+obj_counter)= SpeedBoost(args...);

for simplicity sake objcounter is 0 for now

MObject has
virtual Draw();

SpeedBoost has
Draw();

when I attempt to run through a list of objects and call:

objects[loop1].Draw();

the SpeedBoost::Draw() is never called
only the MObject::Draw() is called.

I need it to call the implementation of the Draw for the class that it is not the base class draw.

I'm know that implementing a linked list would probably work but that's added complexity for something that an array should hopefully be able to do.

Share this post


Link to post
Share on other sites
[quote name='Vero' timestamp='1343333709' post='4963420'][code]*(object+obj_counter)= SpeedBoost(args...);[/code][/quote]

You are [url=http://en.wikipedia.org/wiki/Object_slicing]slicing[/url] your [tt]SpeedBoost[/tt] here. The temporary [tt]SpeedBoost[/tt] you create here is truncated to a [tt]MObject[/tt] so it can fit inside the [tt]MObject[/tt] array. Try the following code:

[code]
// Create
MObject** objects = new MObject*[num_of_obj];

// Use
objects[0] = new SpeedBoost( ... );
objects[1] = new OtherObject( ... );

// Cleanup
for ( ... )
delete objects[idx];
delete[] objects;
[/code]

Share this post


Link to post
Share on other sites
Ok that worked perfectly, and I understand now what slicing is but I don't quite understand why the solution works.

lets see if I can figure it out. MObject** is a pointer to an array of pointers so it's allocates a slot to each pointer but doesn't allocate them a type yet? so now that they have not be allocated a type we can use new to allocate them to the correct class?

Share this post


Link to post
Share on other sites
[quote name='Vero' timestamp='1343335764' post='4963431']MObject** is a pointer to an array of pointers so it's allocates a slot to each pointer but doesn't allocate them a type yet? so now that they have not be allocated a type we can use new to allocate them to the correct class?[/quote]

Yes. Keep in mind that [tt]MObject[/tt] is an [tt]MObject[/tt] itself, while a [tt]MObject*[/tt] (or [tt]MOjbect&[/tt]) is the [tt]MObject[/tt] interface of an [tt]MObject[/tt] or one of its derived types.

Share this post


Link to post
Share on other sites
[quote name='dimitri.adamou' timestamp='1343340703' post='4963446']
I may be wrong - you need to specify that SpeedBoost is also a virtual function. Thats why its never called
[/quote]
I could be wrong too but alternatively if there's no Mobject draw() method as such he could make it pure virtual by appending =0

Share this post


Link to post
Share on other sites
A slightly more idiomatic C++ solution:

[code]class Object
{
public:
virtual void Display() const = 0;
};

class IntObject : public Object
{
public:
explicit IntObject(int value)
: MyValue(value)
{ }

virtual void Display() const
{
std::cout << "Integer object: " << MyValue << std::endl;
}

private:
int MyValue;
};

class StringObject : public Object
{
public:
explicit StringObject(const std::string& value)
: MyValue(value)
{ }

virtual void Display() const
{
std::cout << "String object: " << MyValue << std::endl;
}

private:
std::string MyValue;
};

int main()
{
// Also try boost::shared_ptr or boost::scoped_ptr if you're on an outdated compiler
typedef std::vector<std::unique_ptr<Object>> ObjectContainer;
ObjectContainer objects;

// Create us some objects!
objects.push_back(new IntObject(1));
objects.push_back(new StringObject("test"));

// Do stuff the C++11 way
std::for_each(
std::begin(objects),
std::end(objects),
[](const std::unique_ptr<Object>& obj)
{
obj->Display();
}
);

// Alternately, the C++98 way
for(ObjectContainer::const_iterator iter = objects.begin(), end = objects.end(); iter != end; ++iter)
{
(*iter)->Display();
}

// Note: no need to explicitly delete anything!
}[/code]


My C++11 is still getting into shape, so feel free to nitpick on that bit :-) Also, [i]please[/i] feel free to ask any questions this code might raise!


(Note also that this could be done with templates instead of manually creating an IntObject and StringObject, but that seems a bit excessive in the context of the OP's question.) Edited by ApochPiQ
Accidentally a void.

Share this post


Link to post
Share on other sites
[quote name='ApochPiQ' timestamp='1343354399' post='4963482'][code]
// Do stuff the C++11 way
std::for_each(
std::begin(objects),
std::end(objects),
[](const std::unique_ptr& obj)
{
obj->Display();
}
);
[/code][/quote]
I can't help but feel it's clearer to write it this way (still C++11).
[code]
for (const auto& obj: objects)
{
obj->Display();
}
[/code]

Share this post


Link to post
Share on other sites
ApochPiQ:

I think you meant

[CODE]
objects.push_back(std::unique_ptr<Object>(new IntObject(1)));
objects.push_back(std::unique_ptr<Object>(new StringObject("Test")));
[/CODE]

Or maybe not, if your compiler doesn't force you to be so verbose. VS10 does.

Also you forgot to give Object a virtual dtor.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this