Jump to content
  • Advertisement
Sign in to follow this  
darenking

C++ friend classes dilemma

This topic is 4838 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

I have a class called World that stores all data for my game, and I'm creating a class called Creator that loads bitmaps and other data into World's vectors. Originally, the instance of Creator was a member of World, and World passed pointers to its vectors to Creator. Creator stored these pointers and dereferenced them when it needed to add bitmaps or whatever to the vectors. But I realised I was passing a huge number of pointers. So, I have decided to make Creator a friend of World so that it can see World's vectors directly. I have had a few problems with this. Firstly, I wasn't sure if Creator should be a member of World, or should sit alongside World? Currently I have both Creator and World as members of another object, my game engine called Engine. // in Engine.h World m_World; Creator m_Creator; (I have also tried creating the Creator instance with 'new', but the problem is still the same.) It all compiles and runs fine, except, bizarrely, although there should be just one instance of World, there is now two instances. One is created and then destroyed. Then the other is created, destroyed at the end of the program. (I know this because the constructor and destructor for the World object log messages to a log.txt file.) The first instance of World is created when the program first runs, then destroyed after the first call to Creator: m_Creator.File(m_World); After this call, m_World is destroyed along with all of the bitmaps that Creator put into its vectors. m_World is then created again the next time one of its methods is called: m_World.Update(); Naturally it is then destroyed at the end of the program. Hmm... What could be causing the destruction and reconstruction of the m_World object? Maybe it could be to do with the following, which I read somewhere on the net, which follows an example of friend classes:
Quote:
You may also see something new in the first instruction of the program, that is the empty prototype of class CSquare. This is necessary because within the declaration of CRectangle we refer to CSquare (as a parameter in convert()). The definition of CSquare is included later, so if we did not include a previous definition for CSquare this class would not be visible from within the definition of CRectangle.
The example that it gives, with square and rectangle objects, has everything in one file. Of course my program has each class in its own .h and .cpp files. If I need an empty prototype of one of the classes (World?) where should I put it? Or is it some other problem? [Edited by - darenking on August 20, 2005 11:45:09 AM]

Share this post


Link to post
Share on other sites
Advertisement
I'm not sure how to fix your real problem (sounds something with temporaries to me). But from your explanation, I feel another approach is much more intuitive to me.

If I understand correctly, the only task of Creator is to create the World you are using in your engine, right? How about this approach then?

// in Engine.h
World *m_world;

// in Creator.h
class World;
static World *createWorld( std::string const &filename );

// in World.h
friend class Creator;

// in Engine.cpp somewhere..
m_world = Creator::createWorld( "utopia.wrl" );

Share this post


Link to post
Share on other sites
Quote:
Original post by darenking
m_Creator.File(m_World);

After this call, m_World is destroyed along with all of the bitmaps that Creator put into its vectors.

m_World is then created again the next time one of its methods is called:

m_World.Update();

Naturally it is then destroyed at the end of the program.

Hmm...

What could be causing the destruction and reconstruction of the m_World object?

I can't tell without a see a declaration for Creator, but it looks like your passing m_world to Creator::File() by value, which means the compiler will make a copy (it makes a new object and calls its copy constructor) of m_world. When Creator::File() returns, the copy of m_World goes out of scope, thus getting destructed.

:stylin: "Make games, not war."

Share this post


Link to post
Share on other sites
depending on what Creator::File() does, you'd declare it like this:

bool Creator::File( const World& world, int level ); // if world doesn't get modified
bool Creator::File( World* world, int level ); // if world does get modified


:stylin: "Make games, not war."

Share this post


Link to post
Share on other sites
World should be modified by Creator; that's Creator's job, to load in bitmaps, chop them up into individual animation frames and store them in the member vectors of World.

As regular readers of my pleas for help will know, I often get the concept but can't do the syntax.

Let's say the World object is a member of Engine, declared in engine.h like this:

World m_World;

And let's say I want to also declare the Creator object in Engine, but I want to declare it dynamically so that I can delete it when its done its job. I create it like this and try call its main method, File, like this:


Creator* creator = new Creator();
if ( creator->File(m_World) )
{
m_TempIsWorldCreated = true;
delete creator;
}




Clearly I am sending m_World by copy. How do I send it by reference (or pointer) so that creator alters the same instance of the World class, m_World?

Here's the declaration for Creator::File...

bool File(World world);

Share this post


Link to post
Share on other sites
Quote:
Original post by darenking
How do I send it by reference (or pointer) so that creator alters the same instance of the World class, m_World?

Here's the declaration for Creator::File...

bool File(World world);


bool Creator::File( World* world ); // accepts a pointer to a World object that can be modified

...

Creator* creator = new Creator();

if ( creator->File( &m_World ) ) { // passes m_World's address to creator::File()
m_TempIsWorldCreated = true;
delete creator;
}

:stylin: "Make games, not war."

EDIT: edited for clarity

Share this post


Link to post
Share on other sites
stylin, I like your style.

I tried to work it out myself before you posted, and I got it all correct apart from I put the & on the wrong side of m_World.


Final thing, within the Creator class, I have to dereference the pointer like this when I want to access the vectors in the m_World object:

(*world).m_Sprites.push_back(create_bitmap(50, 50));

It works but it seems a bit messy. Is there a simpler way?

I also need to store this pointer as a data member of Creator, as it will be used by every method in the class. I can store it like this:

// in creator.h
World* m_pWorld;

// in creator.cpp
m_pWorld = world;

Maybe there is a better way that doesn't involve having to deref the pointer every time I use it?

Share this post


Link to post
Share on other sites

bool Creator::File( World& world ); // accepts a reference to a World object that can be modified

...

Creator* creator = new Creator();

if ( creator->File( *m_World ) ) { // passes m_World to creator::File()
m_TempIsWorldCreated = true;
delete creator;
}

Dereference it once in the function call. Creator::File() will then create new alias (reference) to m_World everytime this is called. Then just use member notation without dereferencing it.

:stylin: "Make games, not war."

Share this post


Link to post
Share on other sites
Quote:
Original post by darenking
I also need to store this pointer as a data member of Creator, as it will be used by every method in the class. I can store it like this:

..

Maybe there is a better way that doesn't involve having to deref the pointer every time I use it?

What does Creator::File() do exactly?

:stylin: "Make games, not war."

Share this post


Link to post
Share on other sites
Creator loads in a file called 001.txt (for level 1, for example), and processes a list of commands, like these below:


NameSpriteNext { <spriterobotlegs> }
ImageLoadSprite { <data/robofun_sprites_robot_legs_bigfile_01_rotatefromcentre.bmp> }
ImageCopyFlipHorzSprite { (spriterobotlegs+1) , 8 }
NameSpriteNext { <spriterobottop> }
ImageLoadSprite { <data/robofun_sprites_robot_top_05.bmp> }
ImageCopyFlipHorzSprite { (spriterobottop+1) , 2 }




I thought I should put the code that interprets these commands in a separate object, otherwise the World object will end up huge. It's a complex system but enables me to have my sprites created from masks and textures. It's very flexible and works very well.

Creator deals with loading in the text file (which is why it is sent the current game level), reading the commands, parseing, and then processing the commands, loading in the masks and textures, and cutting out coloured shapes from these textures using the masks. For example it will make lots of different shades and shapes of human faces, hair, arms etc and stick them together to make lots of different characters for the game.

I can delete the Creator object and World is nonethewiser, it just finds itself stuffed full of goodies.

[Edited by - darenking on August 22, 2005 9:37:17 AM]

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!