Archived

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

neonfaktory

How To: Make Classes Include Eachother (or achieve the same effect)

Recommended Posts

Ok, I've tried in multiple places to find this answer and it always gets skimmed over or a pre-recorded response (seems like it) or abstracted and ultimately not answered. SO, I'm not even going to post any of my code, I would just like to know the DEFINATIVE way, if possible, to have classes inlcude eachother. The result I would like is to take Class A and inlcude Class B so that I can access Class B's members from within Class A, and then take Class B and include Class A as well, so I may access Class A's members from within Class B. Being able to do so would simplify my program and get rid of this headache, so any help on this would be greatly appreciated . [edited by - neonfaktory on May 1, 2003 11:31:04 AM]

Share this post


Link to post
Share on other sites
I think you wanna make them friend classes, so :


  

class A;

class B
{
private:
// some data members

public:
// some functions


friend class A;
};

class A
{
private:
// some other stuff

public:
// some other functions


friend class B;
};



I think that should work.


Share this post


Link to post
Share on other sites
you can only include class B in class A, or vice versa, through pointers. You can''t include an object of class B in class A if B is not yet defined.

class A;
class B;

class A
{
B hello; // error, compiler doesn''t know how big B is
B* phello; // OK
};

class B
{
A world; // OK, A has been defined before
A* pworld; // OK
};



Current project: A puzzle game.
% completed: ~0%
Status: Active.

Share this post


Link to post
Share on other sites
Oops, I should have mentioned "without forward declaration". Forward declaration isn't enough, because whichever class is using the forward declaration doesn't really have full access like inlcuding does. As far as I've been able to achieve, whichever class gets the forward declaration can only get a pointer to a member of the forward declared class, and even then it can't even use the member functions of that class. I have to use some "abstracting" class that has access to both the classes just to be able to manipulate and use the information. Thanks for the response, though. I'm really just looking for an 'absolute include' here, forward decs dont seem to cut it, unless I'm using them incorrectly...

*EDIT* In response to Moagly's code, I didn't notice the "friend" keyword/line in there - does that change the way forward declarations work somehow? I'm not familiar with it *goes to look it up*...

*EDIT* Well, I just looked it up and, from the SOUNDS of it, by "friending" the classes together should allow me to use the data members how I want to. If this is the case, thanks a ton, if not... umm, I'll be back

[edited by - neonfaktory on May 1, 2003 11:42:47 AM]

[edited by - neonfaktory on May 1, 2003 11:46:26 AM]

Share this post


Link to post
Share on other sites
The size of an object created by a class is related to the size of its member variables. If A contains B and B contains A then you can't determine the size. If A is made up of B and some extra bit (xa) then:

sizeof(A) = sizeof(B) + xa

If B is made up of A plus some extra bits (xb) then:

sizeof(B) = sizeof(A) + xb
i.e.
sizeof(B) = sizeof(B) + xb + xa
This is recursive and will go on forever. Your compiler won't like it as it needs to know the size of classes to be able to assign memory, dereference pointers, etc, etc. All of that has to be known at compiler time.

I'm not really sure how you're imagining these two classes interacting. Do you mean one class can use the static functions of another class. For there to be any more meaningful relationship you have to be talking about a particular instance of class A communicating with another particular instance of class B.

Are you talking about classes or class instances (ie objects)? Do you have a java background and think you can use inner classes?


[edited by - petewood on May 1, 2003 11:56:25 AM]

Share this post


Link to post
Share on other sites
Oh, I totally understand the reasons why you can''t LITERALLY do it. I was just wondering if there was some way to achieve the same effect of having access to eachother''s members equally. Now that I THINK I have the answer (using friend declarations), I''ll tell you what I''m trying to do...

I have a "Player" class that handles YOU, as the, well, player. For example, it accepts the input that a different Input class extracted and then passes it on to the Ship class as simplified commands like "THRUST" or "SHOOT".

So, "Player" obviously has to include Ship so it can "AddControl()" and so on, but the problem comes when "Ship" needs to know of some information the Player class is holding; Namely, the Player class holds the custom statistics information, like the Max Health, Shields, Speeds, etc., since each Player will have thier own custom attributes.

Naturally, you would include "Player" in "Ship" so you use Ship member function "Player* myPlayer" to be like "myPlayer->MAX_HEALTH.value" for special calculations that Ship handles, like recharging health to the max level that Player has. However, this wouldnt work because "Player" already has "Ship" included so it can "control" your Ship accordingly.

Simple forward declaration doesn''t seem to work either. I *CAN* have a "Ship" member function "Player* myPlayer", but (I assume) because "Player" isn''t truely included in Ship, I can''t say "myPlayer->MAX_HEALTH.value". So... this means I have to do a simple "recharge" calculation in a different area that has both "Player" and "Ship" included, which is real messy not to mention cumbersome.

Now, if I understand this correctly, I can befriend the classes while using forward declaration and the classes will have access to eachother''s members like I''m hoping? Please say yes

Share this post


Link to post
Share on other sites
It can be done, you have to partition your code correctly into header and implementation files:

in A.h:


    
class B; // fwd declaration


class A {
// everything in here...

B* m_pB;
public:
void doSomethingToB();
};


in B.h:


  
class A; // fwd declaration


class B {
// everything in here...

A* m_pA;
public:
void doSomethingToA();
};


Then inside A.cpp:


  
#include "B.h"

void A::doSomethingToB() {
// do your bidness...

}


Similarly for B.cpp:


  
#include "A.h"

void B::doSomethingToA() {
// do your bidness...

}


Regards,
Jeff

Edit: farging source tags...


[edited by - rypyr on May 1, 2003 1:27:50 PM]

Share this post


Link to post
Share on other sites
And please don''t use "friend" unless you really need to.

Properly expose the data that each class needs in a public method and use those...

Regards,
Jeff

Share this post


Link to post
Share on other sites
quote:
Original post by neonfaktory
So, "Player" obviously has to include Ship so it can "AddControl()"
What does AddControl() do?
quote:

Naturally, you would include "Player" in "Ship" so you use Ship member function "Player* myPlayer" to be like "myPlayer->MAX_HEALTH.value" for special calculations that Ship handles, like recharging health to the max level that Player has. However, this wouldnt work because "Player" already has "Ship" included so it can "control" your Ship accordingly.
I don''t see a reason why it wouldn''t work. Ship has Player* myPlayer, and I assume it''s the owner of the ship, so of course, you can access every public member of Player from Ship. If you have problem with the access (and that''s why you want to use friend keyword), you can make get/set methods or make the members public, or make a public function recharge(), so that Ship can calls it, and Player handles the recharge algorithms.
quote:

Simple forward declaration doesn''t seem to work either. I *CAN* have a "Ship" member function "Player* myPlayer", but (I assume) because "Player" isn''t truely included in Ship, I can''t say "myPlayer->MAX_HEALTH.value".
"isn''t truely included in the Ship"? I don''t know what you mean in here, lol, sorry.

Share this post


Link to post
Share on other sites
You can''t do what you want to do because it would create an infinite loop...what''s to stop you from accessing A::B::A::B::A::B::A:: etc.

Why not just make a new class, C, which contains the class definitions of A and B. Then you can declare C::A or C::B individually, or declare a C and its members.

Why don''t you explain your general problem, if you need to do this you''re concieving of your problem wrong.

Share this post


Link to post
Share on other sites
Ok, now that I'm at work I took another look at this thread and it seems that what rypyr mentioned is what I need. I'll give an update when I can try it out tonight.

In response to alnite:
I was just using "AddControl()" as an example that I need to include "Ship" in "Player" so I can run "Ship" member functions - More specifically, "AddControl()" would be used like "myShip->AddControl(THRUST);" after "Player" gets the inputs from the input handlers.

The other questions you asked go hand in hand - When I forward declare "Player" in "Ship", I can then give "Ship" a pointer to a Player as a member. It doesnt, however, let me use functions of that member. I can't say "myPlayer->anything();", and I assume it is because "Player" is only forward declared, not actually "truely included".

Thanks for the help so far guys

*EDIT* Response to Ishan:
I said before that I understand why I can't do it literally, but that I want achieve the same effect doing so would have. Simple form of what I want to do in case you don't want to read the description I just put up:

I have classes Player and Ship. Not only do they need to know OF eachother, they need to be able to access the data members of eachother (something forward declaration doesn't let you do). For example, Player needs to know of and directly effect Ship it owns (control it) and Ship needs to know the information thier Player holds to determine how it moves (each Player has unique statistics that control thier ships: movement speed, turn rate, etc).
What I need is a method that will allow me to access the data equally back and forth without making accessor class "C" which seems very ugly and cumbersome (to me anyway). But as I said, rypyr seems to have given me the answer I was looking for, which I will try tonight when I get home .

[edited by - neonfaktory on May 1, 2003 2:33:09 PM]

Share this post


Link to post
Share on other sites
You forward declare things in the header so that the compiler is satisfied that the class will exist somewhere. If the linker can''t find the implementation file it will come back with an error.

Forward declarations just decrease dependencies between headers. Don''t get stressed about it - it increases compile speeds.

In the implementation of ''Ship'' (the cpp file) you can include the Player.h file so myPlayer->anything() will be meaningful.

Share this post


Link to post
Share on other sites
Okay, I know you dont want to hear this but trying to make sense of why you cannot use a better method is hard to accept so I just want to get a clear understanding of what you are trying to do. Here is what I understand (or something along these lines):

You have a Player that walks around with all the typical RPG attributes (speed, strength, agility, etc) and he notices a nice shiney new space ship. So you, the player, decide to jump in the space ship (read: you press ''w'' key to move him close, then ''e'' to enter the ship).

Now, this space ship is very intelligent and complex. It performs based on the players characteristics (speed, strength, agility, shields, max health, etc). So the ship reads information about the player, configures itself accordingly, and is ready to be flown (is this a word?). Now, as the player inside the ship, you press ''w'' again to fly the ship forward, as well as other keys to menouver the ship properly.

You then find this ship boring, so you fly it towards a platform, exit the ship with ''e'' and run away from it.


Does this scenario sound similar to what you want to do? (Please correct me if it does not)

Share this post


Link to post
Share on other sites
neonfaktory: you still must make the data members accessible "outside" the class. Hopefully you know how to expose data to the outside world (i.e. you shouldn''t just blindly make all player and ship attributes public).

Hopefully you know this, but if you don''t here is an example.

in Player.h:


class Ship;

class Player {
Ship* m_pShip;
int m_Intelligence;
public:
int getIntelligence() const { return m_Intelligence; }
void flyShip();
};


in Ship.h:


class Player;

class Ship {
Player* m_pPlayer;
int m_ShieldStrength;
public:
int getShieldStrength() const { return m_ShieldStrength; }
void fly();
}


in Player.cpp:


#include "Player.h"
#include "Ship.h"

void Player::flyShip() {
// do something with m_pShip->getShieldStrength() here...
m_pShip->fly();
}


in Ship.cpp:


#include "Ship.h"
#include "Player.h"

void Ship::fly() {
// do something with m_pPlayer->getIntelligence() here...
}


Regards,
Jeff

Share this post


Link to post
Share on other sites
CActor - basic actor class in the universe.
CPlayer - the one and only
CVehicle : public CActor - something the player can drive/fly.
CShip : public CVehicle - cause it is one.
CInputHandler - handles input, pure virtual used as interface
CPlayerInputHandler - active when not in a vehicle
CShipInputHandler - active when in a ship
CCarInputHandler - active when in a car

When the game is first started, the player uses the CPlayerInputHandler to dispatch commands with. When the player enters a vehicle he/she requests the proper CInputHandler from the CVehicle class which a new derived class(i.e., CShip) can override through some virtual method like GetInputHandler.

The Player really only needs to know about a Vehicle. The Ship can know about the Player through the Vehicle base type (SetController(CActor* pActor), note: could be some AI code instead) so that it can adjust its flight characteristics and what not.

This also leads to nice grouping for your Customize Controls view.

[edited by - LordShade on May 1, 2003 5:10:55 PM]

Share this post


Link to post
Share on other sites
Hahaha, ISOPimp, your description sounds funny, but I'll explain it.

Basically, each Player is just an "entity" of someone in the game - I guess kind of a way of leaving the door open for multiplayer. When you respawn, the Ship you will control is then tied to your Player object. Each Player object holds thier own set of attributes, like thrust speed, fire rate, etc., so each Player can create thier own play-style. These attributes determine how the Ship they are controlling performs.

To do this, I have 'Player' accept the input from the input handler and translate it to simplified actions that 'Ship' uses. So when the game loop handles the 'Player's, 'Player" is where it says "if(key == up) myShip->AddControl(THRUST);" When the game loop gets around to handling all the 'Ship's in the universe, it runs through a small queue of controls, acting accordingly.

So like in the example I gave, the Ship you are controlling sees a "THRUST" command, which then should access a "myPlayer" member to do something along the lines of "XVEL += myPlayer->thrust * cosf(angle);" (from the top of my head). Soo, to make this run smoothly I need to have access back and forth between these 2 classes, hence the "Include Eachother (or achieve the same effect)".

Thats just to clear everything up though, as it seems that I already have the answer to my solution (thanks rypyr), but thanks to everybody that helped and gave input. If it doesn't work out right, I'm sure to be back again .

[edited by - neonfaktory on May 2, 2003 12:39:27 PM]

Share this post


Link to post
Share on other sites