virtual functions in derived classes probelm.

Started by
4 comments, last by Waterwalker 14 years, 3 months ago
Hiya guys, This problem I have... I just can't figure why it is giving me a compile error when it looks perfectly fine, as far my my understanding goes. But basically I got a Vehicle class, Spacecraft class, and a Fightercraft class. Vehicle is parent of Spacecraft, Spacecraft is parent of Fightercraft. I have a variable that is a pointer to Vehicle class, but it is actually a Fightercraft class object. When I go to use a method that I know it has access to that exists in the parent class it says that member is not a member of Vehicle. I give you guys a sample of the code

//This code exist in my main application entry point
Vehicle *player = new Fightercraft( "player", &D3DDevice );


And my inheritance for the Vehicle, Spacecraft and Fightercraft is as follows

//----- VEHICLE DEFINITION SECTION -----//
#ifndef VEHICLE_H
#define VEHICLE_H

#include <d3d9.h>
#include <d3dx9.h>

class Vehicle
{
public:
	Vehicle();
	~Vehicle();
	virtual void setID( char *ptrID ) = 0;
	virtual char* getID() = 0;
	
protected:
	char *id;
};

#endif
//------ VEHICLE IMPLEMENTATION SECTION -------//
#include "Vehicle.h"

Vehicle::Vehicle()
{
	id = "id default value: Vehicle";
}

Vehicle::~Vehicle()
{
	delete[] id;
}

//------- SPACECRAFT DEFINITION SECTION --------//
#ifndef SPACECRAFT_H
#define SPACECRAFT_H

#include <d3d9.h>
#include <d3dx9.h>
#include "Vehicle.h"

class Spacecraft: public Vehicle
{
public:
	Spacecraft();
	Spacecraft( char *ptrID );
	Spacecraft( char *ptrID, IDirect3DDevice9 **D3DDevice );
	~Spacecraft();
	void setID( char *ptrID );
	virtual void setMaterial( D3DMATERIAL9 mat ) = 0;
	virtual void createMesh( IDirect3DDevice9 **D3DDevice ) = 0;
	
	char* getID();
	virtual ID3DXMesh* getMesh() = 0;
	virtual D3DMATERIAL9 getMaterial() = 0;
	
protected:
	ID3DXMesh* meshShip;
	D3DMATERIAL9 materialShip;
	const int fuselage;
};

#endif

//--------- SPACECRAFT IMPLEMENTATION SECTION --------
#include "Spacecraft.h"

Spacecraft::Spacecraft(): fuselage( 1 )
{
	id = "id default value: Spacecraft";
}

Spacecraft::Spacecraft( char *ptrID ): fuselage( 1 )
{
	id = ptrID;
}

Spacecraft::Spacecraft( char *ptrID, IDirect3DDevice9 **D3DDevice ): fuselage( 1 )
{
	ZeroMemory( &materialShip, sizeof( D3DMATERIAL9 ));
	materialShip.Diffuse.r = materialShip.Ambient.r = 1.0f;
	materialShip.Diffuse.g = materialShip.Ambient.g = 1.0f;
	materialShip.Diffuse.b = materialShip.Ambient.b = 1.0f;
	materialShip.Specular.r = 0.4f;
	materialShip.Specular.g = 0.4f;
	materialShip.Specular.b = 0.4f;
	materialShip.Power = 8.0f;
}

Spacecraft::~Spacecraft()
{
	if( meshShip != NULL )
	{
		meshShip->Release();
		meshShip = NULL;
	}
}

void Spacecraft::setID( char *ptrID )
{
	id = ptrID;
}

char* Spacecraft::getID()
{
	return id;
}

//----------------- FIGHTERCRAFT DEFINITION SECTION ------
#ifndef FIGHTERCRAFT_H
#define FIGHTERCRAFT_H

#include <d3d9.h>
#include <d3dx9.h>
#include "Spacecraft.h"
#include "ShipWeaponCapability.h"

class Fightercraft: public Spacecraft
{
public:
	Fightercraft();
	Fightercraft( char *ptrID );
	Fightercraft( char *ptrID, IDirect3DDevice9 **D3DDevice );
	void setID( char *prtID );
	void setMaterial( D3DMATERIAL9 mat );
	void createMesh( IDirect3DDevice9 **D3DDevice );
	
	char* getID();
	ID3DXMesh* getMesh();
	D3DMATERIAL9 getMaterial();
};

#endif

//------- FIGHTERCRAFT IMPLEMENTATION SECTION --------
#include "Fightercraft.h"

Fightercraft::Fightercraft(): Spacecraft()
{
	id = "id default value: Fightercraft";
}

Fightercraft::Fightercraft( char *ptrID ): Spacecraft()
{
	id = ptrID;
}

Fightercraft::Fightercraft( char *ptrID, IDirect3DDevice9 **D3DDevice ): Spacecraft()
{
	createMesh( &( *D3DDevice ));
	ZeroMemory( &materialShip, sizeof( D3DMATERIAL9 ));
	materialShip.Diffuse.r = materialShip.Ambient.r = 1.0f;
	materialShip.Diffuse.g = materialShip.Ambient.g = 1.0f;
	materialShip.Diffuse.b = materialShip.Ambient.b = 1.0f;
	materialShip.Specular.r = 0.4f;
	materialShip.Specular.g = 0.4f;
	materialShip.Specular.b = 0.4f;
	materialShip.Power = 8.0f;
}

void Fightercraft::setMaterial( D3DMATERIAL9 mat )
{
	ZeroMemory( &materialShip, sizeof( D3DMATERIAL9 ));
	materialShip.Diffuse.r = materialShip.Ambient.r = 1.0f;
	materialShip.Diffuse.g = materialShip.Ambient.g = 1.0f;
	materialShip.Diffuse.b = materialShip.Ambient.b = 1.0f;
	materialShip.Specular.r = 0.4f;
	materialShip.Specular.g = 0.4f;
	materialShip.Specular.b = 0.4f;
	materialShip.Power = 8.0f;
}

void Fightercraft::createMesh( IDirect3DDevice9 **D3DDevice )
{
	if( FAILED( D3DXCreateBox( *D3DDevice, 2, 2, 2, &meshShip, NULL )))
	{
		MessageBox( 0, "Aircraft::Aircraft( char *ptrID, Engine *ptrEngine, IDirect3DDevice9 *D3DDevice ) - D3DXCreateSphere - FAILED", 0, MB_OK);		
	}
}

void Fightercraft::setID( char *ptrID )
{
	Spacecraft::setID( ptrID );
}

ID3DXMesh* Fightercraft::getMesh()
{
	return meshShip;
}

D3DMATERIAL9 Fightercraft::getMaterial()
{
	return materialShip;
}

char* Fightercraft::getID()
{
	return Spacecraft::getID();
}


Sorry about the long code sample guys. The error I am getting is

error C2039: 'getMaterial' : is not a member of 'Vehicle'


I thought my player variable can reference the getMaterial function because it is inherited by all child classes as well. All the text I've read and all the on-line stuff I research tells me what I am doing should be ok.. yet I don't understand this compile error. I am missing a fundamental concept with this line I think

Vehicle *player = new Fightercraft( "player", &D3DDevice );


Obviously player variable is still being view as a Vehicle object. Sorry about the long post guys :( How can I reference getMaterial function?
Advertisement
You have a Vehicle pointer, so you can only access the interface provided by a Vehicle. However, a Vehicle doesn't provide a method called get getMaterial; that function is only introduced further down the inheritance hierarchy. If you intend all Vehicles to have that method, you need to introduce it in the Vehicle class as well.
In your hierarchy the class Spacecraft is the topmost interface of the hierarchy that declares a function getMaterial. Hence you have to either use this interface for accessing the function or add the function as abstract method to the Vehicle interface.
Spacecraft *player = new Fightercraft( "player", &D3DDevice ); // will compileD3DMATERIAL9 mat = player->getMaterial ();
------------------------------------I always enjoy being rated up by you ...
Hmmm... that makes sense, and I can see the error now.

This actually did cross my mind when I was debugging...

Is it bad practice to have a pure virtual function, in my example getMaterial() to return a variable member that only exist further down the hierarchy, and not in the actual base class itself?

Because in Vehicle, member variable materialShip doesn't exist, it only exists in the the derived Spacecraft class.

I guess my question is more a design question more than anything.

I feel uncomfortable having my getMaterial defined in my Vehicle (interface) class.

I might just change my code to
Spacecraft *player = new Fightercraft();


Thanks guys... sorry about my noobish question.
This is a question about whether every class that inherits Vehicle should have a material or not. If you are absolutely certain that every Vehicle does have one material, then put a pure virtual function in the Vehicle class, that's what they are for ;)

It is not bad design to make use of pure virtual functions in general (though with any feature, it can be misused). For example, my Material class has several pure virtual functions:

class Material{public:virtual QString name() const = 0;virtual QString type() const = 0;virtual RenderNodePtr createRenderNode() const = 0;};


In general I don't care how a specific material implements that stuff, however I do care that every material implements it. For example, every material should be able to create a RenderNode, which is nothing more than a collection of a specific shader, as well as all parameters that are given to the shader and rendering pipeline (that is: draw mode, textures, matrices, whatever...).
Quote:Original post by tenpoundbear
I feel uncomfortable having my getMaterial defined in my Vehicle (interface) class.
... sorry about my noobish question.

Quite intelligent questions after all because they influence whether you end up with bad design or not. So better to ask than to do and learn the hard way [grin]

It is actually good design because placing this function in the Vehicle base class lets you avoid dynamic_casts if you need to call this function. If all classes you derive from Vehicle have a material then move the whole material attribute into the Vehicle class.

It gets more tricky if not all classes you plan to derive have a material. Then it even makes sense to introduce another function in your Vehicle class:
class Vehicle {  //blablabla  virtual bool hasMaterial () const { return false; } virtual D3DMATERIAL9 getMaterial () { return D3DMATERIAL9 (); }};

This way you can work with Vehicle pointers and ask each vehicle if it really has a material. Derived classes that do not have a material do not override those two methods. They will tell you that they do not have a material and if you get it from them anyway its your own fault.

Derived classes that do have a material must override both functions. The first to return true and the second one to return the correct material.
------------------------------------I always enjoy being rated up by you ...

This topic is closed to new replies.

Advertisement