Overloading virtual function w/ new parameter

Started by
7 comments, last by startreky498 18 years, 10 months ago
I have a class called Graphics. I also have a class called "dx9" which is a child class of Graphics. Graphics will contain all the functions that dx9 will use, only empty. (as they will be defined in dx9) I also have a class called SPRITE. Its child is called "d3dSprite". SPRITE contains only an empty Release() function which will need to be inherited later. My original intent was to make pretty much all functions in Graphics virtual. I also wanted to make the Release function in SPRITE virtual. Several problems have arrisen, but I'll deal with one of my most prevalent issues first. Say I've got a function in Graphics: virtual void drawImage(SPRITE* sourceImage); Now for the dx9 class, I could do one of three things: I could change the parameter (which produces some problems), or I could typecast sourceImage to d3dSprite, or I could use the address of sourceImage in a temporary d3dSprite. So which method seems best? I'm thinking I want to use the first method, since I won't have to do so much extra work in the function. Plus I've had bad luck with static_cast. And on a side note, if one can tell me how to go about actually doing the first method, I would be much appreciative.
Advertisement
Quote:Original post by startreky498
Now for the dx9 class, I could do one of three things: I could change the parameter (which produces some problems)

This would result in a different function rather than overloading an existing one. You might as well not bother with the inheritance in this case.
Quote:
or I could typecast sourceImage to d3dSprite

This is what you want. Except use dynamic_cast to do the conversion, not static_cast.

CM
Ok that sounds good. But when I call the release function at the end of the code, it would look like this:

tempSprite.Release()

Since tempSprite is of class SPRITE, this calls up the empty Release function (causing an uninstantiation error). Under the current design scheme, the actual sprite needs to be of class SPRITE, not d3dSprite, since I may want to use a class other than d3dSprite in the future and I don't want to change my code with every different class.

How should I go about declaring a sprite like tempSprite to be of class SPRITE but still relate its Release functionality to d3dSprite?
Quote:Original post by startreky498
Ok that sounds good. But when I call the release function at the end of the code, it would look like this:

tempSprite.Release()

Since tempSprite is of class SPRITE, this calls up the empty Release function (causing an uninstantiation error).

You can't create an object of class SPRITE if Release is a pure virtual function [as you seem to imply...if it isn't, it sounds like it should be]. You create SPRITE*, and point it to an object of type d3dSprite, and the compiler sees to it that the proper function is called. Yes, if you create a SPRITE object, it will call SPRITE functions. Once you make that copy, you lose all your d3dSprite information, because SPRITE objects have noplace to put them. That's why you don't do that if you are trying to mainting your inheritance information.

CM
It sounds like you have a design problem. What is a sprite? What additional functionality does a d3dSprite have over a sprite? You say you may want to have a different class in the future. How would that class differ from sprite and d3dsprite? Identify the common state and behaviour, what does it really mean to be a sprite? This then goes in the base sprite class. This is your base sprite contract. You are declaring that every sprite you ever use will have this state and behaviour (or variants of it). For example you might decide that every sprite will have a height and width and will be drawable, but this is the limit of the common behaviour:
class Sprite{	public:		virtual ~Sprite();		virtual unsigned int width();		virtual unsigned int height();		virtual void draw() = 0;	private:		unsigned int width_;		unsigned int height_;};

Now you might decide that a d3dsprite also has a texture:
class D3dSprite	:	public Sprite{	public:		D3dSprite(Texture texture);		virtual void draw();	private:		Texture texture_;};

Now your D3dSprite class handles all of the texture related issues - binding the texture, enabling texturing, ensureing the correct texture state is set, etc. Your drawImage function is now simple:
virtual void drawImage(Sprite * sourceImage){	sourceImage->draw();}

Your compiler will now enforce the contract you have specified. Each sprite can choose exactly how it is drawn, but they must all be drawable.

I know this is not exactly what you asked, but your alternative is to do type-switching:
if (SomeType * someType = dynamic_cast< SomeType >(sourceImage)){	// do something for SomeType}else if (SomeOtherType * someType = dynamic_cast< SomeOtherType >(sourceImage)){	// do something for SomeOtherType }

Type-switching is extremely brittle and offers no advantage over virtual functions. You could (and should) simply rewrite the above as a doSomething virtual function, since your compiler will then enforce it for future types.

Enigma
That does look good. Here's a little more specific code that can show you my problem here:

class SPRITE{public:	virtual void Release()=0;};class d3dSprite: public SPRITE{public:	VERTEX mVertex[4];	LPDIRECT3DTEXTURE9 spriteTexture;	void Release();};SPRITE* dx9::registerImage(char *path, transparency color){	d3dSprite tempSprite;	ZeroMemory(&tempSprite, sizeof(d3dSprite));	//Fill up tempSprite with information	return &tempSprite;}int WINAPI WinMain(...){	Graphics* graphics = new dx9();	//...//	SPRITE* tempSprite = new d3dSprite();	tempSprite = graphics->registerImage("bitmap.bmp",none);	//...//}


Now I want to retain all the information in d3dSprite for when I want to call my draw function. I think I've figured out a generic way of doing the following:
SPRITE* tempSprite = new d3dSprite();

So that's not an issue right now. Anyway, I believe I'm losing class information after the return. Without forcing us all to go through too much detail, if this has a simple solution, is there anything obviously wrong with my register function?
Quote:Original post by startreky498
Anyway, I believe I'm losing class information after the return. Without forcing us all to go through too much detail, if this has a simple solution, is there anything obviously wrong with my register function?


Yup, there are two major problems. First, you create the d3dSprite object on the stack, so it will be destroyed as soon as your function goes out of scope. Hence, your object will be junk. Secondly, you're using ZeroMemory on class with virtual functions. Don't ever do this! On top of zero-ing out your member variables, it's also going to zero-out the v-table for your class, effectively trashing it's mechanism for calling any virtual functions, like Release.

Instead of doing this, when working with C++ you should prefer to use the initializer list in the constructor of your class to set your member variables to logical defaults. Not only is this safer, since you don't have to worry about whether you can call ZeroMemory on a class or not, but it's cleaner since you only write the code once.

Anyway, your code should look something like this instead:

SPRITE* dx9::registerImage(char *path, transparency color){	d3dSprite* tempSprite = new d3dSprite;	//Fill up tempSprite with information	return tempSprite;}


Hope that helps!

-John
- John
Quote:Original post by startreky498
So that's not an issue right now. Anyway, I believe I'm losing class information after the return.

No, the information is all still there. You just can't access it except through members exposed by the SPRITE interface. That is what interfaces are for, Enigma's post was a pretty good one.
Quote:Original post by startreky498
Without forcing us all to go through too much detail, if this has a simple solution, is there anything obviously wrong with my register function?

Yeah. Not only do you leak memory [the line directly before register], but you return an address to local memory. That's a huge no-no. It might work great right up until the next function you call. Then everything'll go to hell.

CM
Thank you all soooooooooo much! Many many props to all who helped me on this post. It works! :D

This topic is closed to new replies.

Advertisement