Polymorphism and pointer arrays in c++

Started by
8 comments, last by Amnesty2 11 years, 8 months ago
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.
Advertisement
*(object+obj_counter)= SpeedBoost(args...);


You are slicing your SpeedBoost here. The temporary SpeedBoost you create here is truncated to a MObject so it can fit inside the MObject array. Try the following 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;
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?
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?


Yes. Keep in mind that MObject is an MObject itself, while a MObject* (or MOjbect&) is the MObject interface of an MObject or one of its derived types.
I may be wrong - you need to specify that SpeedBoost is also a virtual function. Thats why its never called

I may be wrong - you need to specify that SpeedBoost is also a virtual function. Thats why its never called

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
A slightly more idiomatic C++ solution:

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!
}



My C++11 is still getting into shape, so feel free to nitpick on that bit :-) Also, please 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.)

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]


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

I can't help but feel it's clearer to write it this way (still C++11).

for (const auto& obj: objects)
{
obj->Display();
}

Stephen M. Webb
Professional Free Software Developer

Yeah, ranged for is also an option. I can't remember how widespread compiler support is, though, so I usually don't think of it offhand.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

ApochPiQ:

I think you meant


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


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.

This topic is closed to new replies.

Advertisement