Sign in to follow this  
tenpoundbear

Forward Declaration Problem

Recommended Posts

Hey guys, I got this problem where I am trying to use forward declaration instead of "#include..." but it is giving linker error at compile time. If I use #include..." then my application compiles fine. I don't quite understand it... was hoping you guys will able to tell me where I am going wrong with my forward declaration. This is my Vehicle, Spacecraft and Fightercraft classes
//----- 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 "Vehicle.h"
class ShipWeaponCapability; // THIS GIVES ME ERROR
//#include "ShipWeaponCapability.h" // THIS COMPILES FINE

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

#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, int arraySize ): fuselage( 1 )
{
	setID( ptrID );
	weapon = new ShipWeaponCapability[ arraySize ];
}

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 "Spacecraft.h"

class Fightercraft: public Spacecraft
{
public:
	Fightercraft();
	Fightercraft( char *ptrID );
	Fightercraft( char *ptrID, IDirect3DDevice9 **D3DDevice );
	void setID( char *prtID );
	void setMaterial();
	void createMesh( IDirect3DDevice9 **D3DDevice );
	void initWeapon();
};

#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::initWeapon()
{
	// Empty function for now.
}

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

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

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

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



This is my ShipWeaponCapability class
//-------- DEFINITION SECTION ------------
#ifndef SHIPWEAPONCAPABILITY_H
#define SHIPWEAPONCAPABILITY_H

#include "Equipment.h"

class ShipWeaponCapability {
public:
	ShipWeaponCapability();
	void initialiseWeaponTypeAllow( int arraySize, Equipment **equipment );

private:
	Equipment *weaponEquiped;
	char **weaponTypeAllow; //The id of the equipment will be stored in this array of characters.
};

//-------- IMPLEMENTATION SECTION ----------
#include "ShipWeaponCapability.h"

ShipWeaponCapability::ShipWeaponCapability()
{
	weaponEquiped = 0;
}

void ShipWeaponCapability::initialiseWeaponTypeAllow( int arraySize, Equipment **equipment )
{
}
#endif



I've left out lots of code because there are lots of classes in my application. But I gave code samples for the important classes. Basically the error I am getting is...
Quote:
1>c:\users\icicle\desktop\celestrial wars\starfleet_v12\starfleet\spacecraft.cpp(21) : error C2512: 'ShipWeaponCapability' : no appropriate default constructor available 1>c:\users\icicle\desktop\celestrial wars\starfleet_v12\starfleet\spacecraft.cpp(72) : warning C4150: deletion of pointer to incomplete type 'ShipWeaponCapability'; no destructor called 1> c:\users\icicle\desktop\celestrial wars\starfleet_v12\starfleet\spacecraft.h(13) : see declaration of 'ShipWeaponCapability'
But if I change my forward declaration line in my Spacecraft header class to #include "ShipWeaponCapability.h" it will compile perfectly fine. I just wanted to try this concept out... it won't hurt me to use "#include..." instead but I just wanted to learn and understand why it wasn't working in my case. Many thanks in advance guys.

Share this post


Link to post
Share on other sites
If you're using one header for interface and implementation, then it's no wonder.
Split them up into .h and .cpp files. In your .h files, use forward declarations. In the .cpp files, you have to #include the class declaration because otherwise the compiler can't know which functions/members your forward declared class supports.

Share this post


Link to post
Share on other sites
Even if you forward declare a class, you still have to include the actual definition somewhere in the compilation unit. Try adding #include "ShipWeaponCapability.h" in Spacecraft.cpp right after #include "Spacecraft.h". That should solve the problem.

Share this post


Link to post
Share on other sites
You need to #include "ShipWeaponCapability.h" in spacecraft.cpp.

The header spacecraft.h doesn't need the full definition of ShipWeaponCapability, and so your forward declaration is working as expected.

However, when you attempt to new a ShipWeaponCapability in the source file spacecraft.cpp, you DO need the full definition. #include'ing in that definition will fix the compile errors.

Share this post


Link to post
Share on other sites
You classes could be simplified greatly by taking advantage of the C++ standard library, particularly std::string and std::vector, and using smart pointers to manage pointer members.

For example, you have a bug in your application where you potentially call delete [] on a string literal, which is undefined behaviour.

Share this post


Link to post
Share on other sites
The definition and implementation section do reside in separate files, .cpp and .h respectively.

Sorry I didn't mention that. I just didn't separate them in this post cause I was trying to be neat, but rest assured they are in separate files.

So what mattd said... because I used the 'new' in the source file that is pretty much why I am getting error?

-----------------------------------------------
Back after modifying my code.

OK I've just followed mattd advise and it works.... but I am wondering why then have a forward declaration in the Spacecraft header file, and also have a #include "ShipWeaponCapability.h" in the Spacecraft implementation file too?

Why not just have #include "ShipWeaponCapability.h" in the Spacecraft header file only.

Maybe my application and classes is a bad example to be using forward declaration on.

Anyways thanks guys for the insight :)

Share this post


Link to post
Share on other sites
Yes, to state it again: the compiler generally must have the full declaration of the class once it's used, in this case it needs to to know if it has a public default constructor for new.
Your use of #include is perfectly sound. Trying to solve it with forward declarations a) won't work and b) won't give any benefit.

Share this post


Link to post
Share on other sites
All you have told the compiler at the point you call new[] (which allocates an array of instances, and therefore will call the default constructor) is that the type exists. Likewise, when you call the destructor using delete the compiler knows nothing about the type. For example, the type might lack a default constructor or the type might have a default constructor via default arguments that might need special handling, it might have a virtual or private destructor. It also needs to know the size of the object for new or new[] expressions.

This is why the compiler must see the class definition when you want to use new or new[] or delete on instances of that class.

Share this post


Link to post
Share on other sites
Quote:
Original post by tenpoundbear
Why not just have #include "ShipWeaponCapability.h" in the Spacecraft header file only.

The usual reason for using a forward declaration is either:

  • For resolving circular dependencies: for example, two classes that need to contain pointers to each other.
  • To speed compilation (what you're using them for here): since you replace the inclusion of ShipWeaponCapability.h in spaceship.h with a forward declaration instead, other compilation units (source files) that include spaceship.h won't include ShipWeaponCapability.h too (unless of course they explicitly include it themselves later on because they need to use ShipWeaponCapabilitys.)


Quote:
Maybe my application and classes is a bad example to be using forward declaration on.

It's fine, see above.

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