Archived

This topic is now archived and is closed to further replies.

Inheritance / Pointers Question

This topic is 5151 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

In my engine, I have a list of objects in a dynamic array of type cGameObject* . From cGameObject, I derive a whole slew of objects, including things like cShip, cProjectile, etc... which I can put into my array (since it is of type cGameObject* ) Each of these objects has some virtual functions. The whole point of the array is to act as a "library" of sorts. When I need a new object, I call a "CreateObject" function with the correct ID and a new object of the correct type is added to the library. I''d like to do it this way, because all the objects read in certain stats from a text file at runtime (things like shields for ships, speed for projectiles, etc... ) Doing this everytime would be prohibitively expensive, so I made an array where I could just copy objects from. That was just the setup, now the problem. The problem is that it doesn''t quite work as I had hoped. I can add new objects to the array, and it works fine. The problem happens when I want to make a new object (a copy, basically) My CreateObject function (as I originally saw it) would deference an object and assign it to a second object, essentially copying it. The second object would be added to a list of all objects in the world. I didn''t think of this beforehand but if I deference the library object to make a copy, I won''t have access to the virtual functions of the correct object anymore. I can''t just assign the pointer of the library object to a new object and add that to the object listing, because I might need the same type of object later on. Any ideas, short of creating a HUGE initializer list, and just reassigning all the variables when I want to make a copy of it from the library list? Is this even a good approach at all? -=Peon=- : A Poorly Implement Forum Bot © Kordova, 2003

Share this post


Link to post
Share on other sites
One suggestion: make the library an array of pointers to objects of your base class. That way, your objects can inherit from a base class but actually be different sizes. When a new library object is added, create a new object and just point to it from your library array.

Also, have the base class have a virtual ''clone'' method. Then, each class should implement that ''clone'', which essentially constructs a new copy of the given object. That way, you can add objects of any type to the list generically, in the base class implementation, by cloning a passed in object that inherits from the base class.

Did that make sense? Sorry, it''s a bit late...

Share this post


Link to post
Share on other sites
quote:
Original post by OldGuy
One suggestion: make the library an array of pointers to objects of your base class. That way, your objects can inherit from a base class but actually be different sizes. When a new library object is added, create a new object and just point to it from your library array.
I THINK that's what I've done so far. My library has an array of GenericObject*, and I've successfully added different inherited types to the list.

quote:

Also, have the base class have a virtual 'clone' method. Then, each class should implement that 'clone', which essentially constructs a new copy of the given object. That way, you can add objects of any type to the list generically, in the base class implementation, by cloning a passed in object that inherits from the base class.
This might be where I'm having trouble. How exactly would I clone an object of type GenericObject*, while maintaining any virtual functions that it might have (since a GenericObject* might actually be a cShip or cWeapon, etc... with unique virtual functions)? I thought I could dereference it and add it to another list, but that kills my virtual functions. If I add the pointer to another list (also of type GenericObject*), I ruin the object in my "library" for future use.

-=Peon=- : A Poorly Implement Forum Bot
© Kordova, 2003


[edited by - Peon on November 6, 2003 1:30:00 AM]

Share this post


Link to post
Share on other sites
To make a clone method, do something like:

virtual BaseClass *Clone();

Then, in each class, create the same function (note that the return type needs to be BaseClass*, even for the derived classes). Then, in each class, do something like:

BaseClass *DerivedClass::Clone()
{
DerivedClass *newOne = new DerivedClass();
*newOne = *this;
return newOne;
}

When you call Clone, it will go to the correct derived implementation (because its virtual), which will create and fill in a new object of your type.

Not sure if this is overkill for what you''re attempting to do. You could always just new the specific objects in some high level function, and pass BaseClass* to the library function that ''stores'' the objects in an array of pointers.

Share this post


Link to post
Share on other sites
It took me awhile to grasp what you''re suggested (my fauly, not yours ), but I think I understand it. I''ll try implementing it tomorrow to see if it works.

Just to make sure...you''re saying is when I want to create a new object from my library, I do something like...

Create()
{
cShip* newShip = Library.Clone()
//other stuff
}

..which will give me a clone of the derived object at Library[i], correct?



-=Peon=- : A Poorly Implement Forum Bot
© Kordova, 2003

Share this post


Link to post
Share on other sites
yeah that should do the trick. I was thinking about the clone function to except I was thinking about implement it like this:



Base* Derived::Clone()
{
return new Derived(*this);
}//Clone




The main difference I think would be that mines would call the copy constructor while OldGuy''s would do the copying using the assignment operator instead. In either case, just make sure those functions are implemented correctly, like for example it''s making deep copies of any pointers a member of the class etc.





--{You fight like a dairy farmer!}

Share this post


Link to post
Share on other sites
If your compiler supports it (VS6 doesn''t), clone() methods are a perfect opportunity to play with covariance.

class Base
{
public:
virtual Base* clone() { return new Base(*this); }
};

class Derived
{
public:
virtual Derived* clone() { return new Derived(*this); }
};

Notice how it can have a different return type. Neato, huh?


How appropriate. You fight like a cow.

Share this post


Link to post
Share on other sites
What they are doing is called a member-wise, or shallow copy. If you don''t have any member data on the free store, then this code is OK. However, If you have data on the free store, and you use this type of copy, it could lead to disaster if the copy cleans up memory which is being used by the original when it dies.

The compiler, as well as providing a default constructor and destructor, provides a default copy constructor as well. what it essentially does is make the pointers (if you have any) point to the same location in memory. compare this to a deep copy, which creates new memory of the type pointed to by the pointers.

here is the syntax to overide a copy constructor in a class:

thing (const thing & someThing )

you could then create new memory, or whatever, in the copy constructor.

Now, that being said, if your memory is being handled by something else, and the classes you copy don''t delete anything on the free store, then i wouldn''t worry about it.

EOF

Share this post


Link to post
Share on other sites
Okay, well I thought I had it working well Then, I created a new object, and set the owner to player 2. Strangely, that ship spun exactly the same as the player 1 ship, despite the fact that my message pump determines who gets the message by the owner. I thought an if-statement might be failing, but nope, couldn't find any.

Then it dawned on me. Running my game through the debugger, I discovered that my, as expected, each of my objects had their own unique address. Is it possible that while each object has a unique address, that the same virtual function is being called for each of the objects? That would explain while my rotation code is only called once, though 4 objects spin on the screen at the same time. I'm at a bit of my loss right now, though I'm somewhat sure that this is the source problem. I didn't quite understand how the last few posts differed from Old Guy's, so perhaps this problem is what they were referring to.

Any suggestions?

EDIT: Solved it... tricky stuff. I was creating a NEW pointer to an animation class, and this pointer was being shared by all the objects that were being created (since it was only called once). I solved the problem with a hacked together solution where I saved the animations, created a new animation class array, and then added the temporary animations back in.

-=Peon=- : A Poorly Implement Forum Bot
© Kordova, 2003

[edited by - Peon on November 9, 2003 3:35:09 AM]

[edited by - Peon on November 9, 2003 4:07:05 AM]

Share this post


Link to post
Share on other sites
quote:
Original post by Sneftel
If your compiler supports it (VS6 doesn''t), clone() methods are a perfect opportunity to play with covariance.



And with templates !


template<class T>
class Cloneable
{
public:
virtual T* clone() { return new T(*this); }
};

class Derived : public Base, public Cloneable<Derived>
{
// stuff

};




[ Start Here ! | How To Ask Smart Questions | Recommended C++ Books | C++ FAQ Lite | Function Ptrs | CppTips Archive ]
[ Header Files | File Format Docs | LNK2001 | C++ STL Doc | STLPort | Free C++ IDE | Boost C++ Lib | MSVC6 Lib Fixes ]

Share this post


Link to post
Share on other sites
quote:
Original post by Fruny
quote:
Original post by Sneftel
If your compiler supports it (VS6 doesn''t), clone() methods are a perfect opportunity to play with covariance.



And with templates !


template<class T>
class Cloneable
{
public:
virtual T* clone() { return new T(*this); }
};

class Derived : public Base, public Cloneable<Derived>
{
// stuff

};



How many compilers will that code actually work on?

And if it does compile, don''t you need to inherit from Base in your Cloneable<> class? Otherwise calling clone() through a Base pointer won''t call Cloneable<>::clone().

Also, you should be very careful not to inherit from a class that derives from Cloneable<>.

Share this post


Link to post
Share on other sites
How many compilers will that code actually work on?

If you drop covariance, I believe VC6 can handle it.

And if it does compile, don''t you need to inherit from Base in your Cloneable<> class? Otherwise calling clone() through a Base pointer won''t call Cloneable<>::clone().

Right.

class Base
{
virtual Base* clone() = 0;
};

template<class T>
class Cloneable : public Base
{
public:
virtual T* clone() { return new T(*this); }
};

class Derived : public Cloneable<Derived>
{
// stuff

};


Also, you should be very careful not to inherit from a class that derives from Cloneable<>.

The compiler should catch the ambiguity.



[ Start Here ! | How To Ask Smart Questions | Recommended C++ Books | C++ FAQ Lite | Function Ptrs | CppTips Archive ]
[ Header Files | File Format Docs | LNK2001 | C++ STL Doc | STLPort | Free C++ IDE | Boost C++ Lib | MSVC6 Lib Fixes ]

Share this post


Link to post
Share on other sites
quote:
Original post by Fruny
How many compilers will that code actually work on?

If you drop covariance, I believe VC6 can handle it.


Just tried it out on VS .NET 2003, and it choked on it. With and without co-variance.

quote:

Also, you should be very careful not to inherit from a class that derives from Cloneable<>.

The compiler should catch the ambiguity.


Hmmm. If I could get it to compile, I''d try calling you on that one.

Share this post


Link to post
Share on other sites
CURSE YOU !


virtual Base* clone()
{
T* original = dynamic_cast<T*>(this);
assert( "class T must derive from Cloneable<T>" && (original != 0) );
return new T(*original);
}


Serves me right for posting code without checking on a compiler.
This works on both VC6 and VC7.1

See http://cpptips.hyperformix.com/cpptips/templ_clone_func


[ Start Here ! | How To Ask Smart Questions | Recommended C++ Books | C++ FAQ Lite | Function Ptrs | CppTips Archive ]
[ Header Files | File Format Docs | LNK2001 | C++ STL Doc | STLPort | Free C++ IDE | Boost C++ Lib | MSVC6 Lib Fixes ]

Share this post


Link to post
Share on other sites
Ok, let''s pretend we have a class that inherits from Derived, say the class DerivedFromDerived. We can''t inherit from Cloneable<DerivedFromDerived>, that would cause an ambiguity error. And if we leave off the inheritance, we have to implement clone() manually, otherwise we''ll slice.

So really, we added a templated code harness to automate a task, but we can only use it once. (Well, only once in inheritance chain of any given leaf class.)

Anyway, from a code maintenance standpoint, it might be better to leave off the template mechanism entirely, so that every class in the hierarchy has a similar "foot print."

As a side note, if you inherit Cloneable<> virtually from Base, it''ll detect the ambiguous inheritance problem at the class definition, which produces a slightly more understandable error message than ambiguity on a specific instance (at least in my opinion).

Share this post


Link to post
Share on other sites
Honestly, I''m not trying to pick on you today. It''s just you have the misfortune to be the only person who seems to be posting code during my current attack of insomnia.

Share this post


Link to post
Share on other sites
Actually, if you cast the return value to Cloneable*, VC7.1 shuts up about the ambiguity in conversion from DerivedFromDerived* to Base*.

return static_cast( new T(*original) );

Now I'm *really* worried about correctness... even though it's an upcast...


Edit: Scratch that, it's bullshit.

And yeah, insomnia here too. Even though I'm (obviously) tired (let's find excuses for crappy code), I can't sleep.


[ Start Here ! | How To Ask Smart Questions | Recommended C++ Books | C++ FAQ Lite | Function Ptrs | CppTips Archive ]
[ Header Files | File Format Docs | LNK2001 | C++ STL Doc | STLPort | Free C++ IDE | Boost C++ Lib | MSVC6 Lib Fixes ]


[edited by - Fruny on November 9, 2003 6:39:53 AM]

Share this post


Link to post
Share on other sites
Yeah, it''s that kind of stuff that made me suggest the virtual inheritance. With normal inheritance it''s possible to get the compiler to shut up until you actually use clone() or cast to Base *.

Share this post


Link to post
Share on other sites