C++ friend classes dilemma

Started by
28 comments, last by stylin 18 years, 8 months ago
Just have a pointer to Creator in the private: section of World if you don't want to nest classes. You can still compile the two seperately. [smile]

:stylin: "Make games, not war."
:stylin: "Make games, not war.""...if you're doing this to learn then just study a modern C++ compiler's implementation." -snk_kid
Advertisement
Forgetting all the nesting stuff for the moment...

If I go down the route of having two separate objects, the World and the Creator objects, and Creator is a friend of World...

Do I have to create them both in, say, Engine, or can I create World as a member of Engine and then create Creator as a member of World? (Using 'this'.)

Even if I can, though, not sure if there's any need to. Though I guess in a way it's more logical. I mean, the game Engine wants to create World, it's not going to want to care about how World fills itself with goodies.

Quote:Original post by darenking
If I go down the route of having two separate objects, the World and the Creator objects, and Creator is a friend of World...

Do I have to create them both in, say, Engine, or can I create World as a member of Engine and then create Creator as a member of World? (Using 'this'.)

If Creator is a member of World, then [snip] it would be redundant to make it a friend class, since Creator already has a referece to World from its constructor (see my example above). It would already have access rights to World, being a member of the class.

Quote:Even if I can, though, not sure if there's any need to. Though I guess in a way it's more logical. I mean, the game Engine wants to create World, it's not going to want to care about how World fills itself with goodies.

Exactly - that's data hiding, that's OOP. And, IMO, it's proper in this case, as Creator is nothing more than an entity loader, and it's not depending on anything from Engine to function.

:stylin: "Make games, not war."

EDIT: clarification
:stylin: "Make games, not war.""...if you're doing this to learn then just study a modern C++ compiler's implementation." -snk_kid
I'm trying to just have the instance of Creator as a member of World, in the same way that World is a member of Engine, ie not as a nested class... as I don't really understand why it should be nested, when World isn't nested inside Engine.

There's a circularity problem though: world.h needs #include "creator.h", and creator.h needs #include "world.h", which of course doesn't compile.

Is this something to do with why it would need to be a nested class?

I mean, world.h is a member of engine.h, but that's not a problem because world.h doesn't need to modify engine.h or even know of its existence.



Quote:Original post by stylin
Assuming creator is called once in the beginning of the program, then is immediately deleted after it sets world up, then you should make it a private member of world, and construct it in world's contructor initializer list.


Actually Creator's job is to create the current game level, so it needs to fill World with goodies at the start of each level. Part of why the way World is filled is so complicated is that my monster sprites and whatever are constructed out of masks and textures so that they can be different each time, randomizing the colours for example, or choosing randomly from dozens of head shapes.

So World definitely needs to call Creator in one of its methods - probably in World::Update(), after checking to see if you've just started a new level.

With this in mind, does Creator still need to be a nested class? (And do nested classes need to be friends? I guess not.)

I'd much rather avoid nested classes, if I can, but need to deal with that circularity problem.

* Maybe I can use the extern keyword in World to tell it about Creator? *
Quote:Original post by darenking
Actually Creator's job is to create the current game level, so it needs to fill World with goodies at the start of each level. Part of why the way World is filled is so complicated is that my monster sprites and whatever are constructed out of masks and textures so that they can be different each time, randomizing the colours for example, or choosing randomly from dozens of head shapes.

OK, was under the impression that Creator was in part responsible for creation of World, and played no role in dynamically changing World's contents, as it seems it does. So, if creating a new World every level isn't an option for you, then I would keep Creator as a member of Engine. That would solve the circularity problem, since Engine could call Creator's methods with a pointer or reference to World, for instance, and World wouldn't be any the wiser.

Have Creator's constructor accept a reference to World, and create a new Creator with:
Creator creator = new Creator( *m_World ); // pass a dereferenced pointer to World


Quote:(And do nested classes need to be friends? I guess not.)

Nesting a class inside of another class makes the nested class local to the enclosing class. All regular scope rules apply here. If the nested class is declared in the private: section, then you can't instantiate it outside of it's container class. As a member of the enclosing class, it has full access to the enclosing class' members.

A class that has a friend declaration inside exposes all of it's members to that friend, regardless of access specification.

Including World.h in your Creator.cpp allows Creator public acces to World's members. Declaring Creator a friend class inside of World exposes the protected and private members as well. There's no circularity problem, since World doesn't interact with Creator at all. In fact, if I understand correctly, Creator's function should be completely transparent to World, so World doesn't even need to know there is a Creator class.

Let me know if I misundertsood your program logic again.

:stylin: "Make games, not war."
:stylin: "Make games, not war.""...if you're doing this to learn then just study a modern C++ compiler's implementation." -snk_kid
Quote:In fact, if I understand correctly, Creator's function should be completely transparent to World, so World doesn't even need to know there is a Creator class.


Hmm, but how can World not know that there is a Creator class if Creator is inside World? And surely it would be World that calls Creator when it needs to be filled up with data and stuff?
Quote:Original post by darenking
Hmm, but how can World not know that there is a Creator class if Creator is inside World?

If Creator is a member of World, then the Engine can call World->Creator->File(). Creator would have full access to World's members.

If you declared Creator to be a friend class of World, Creator has full access to World's members, but is not contained inside World. This is your original scenario:
Quote:So, I have decided to make Creator a friend of World so that it can see World's vectors directly. ... Currently I have both Creator and World as members of another object, my game engine called Engine.


Quote:And surely it would be World that calls Creator when it needs to be filled up with data and stuff?

That depends on your game logic. You could have the engine update World with a Engine->Creator->File( *m_World ) call, or have World update itself with World->Creator->File(). In Either case, World must be told to update from somewhere, and since nothing else besides a text file determines what that update is, I suggested having Creator as member of World (as a privately nested class, so it's invisible outside of World). The Engine can tell World to update itself, and it will do so via Creator. Why does the Engine or anybody else need to have information on Creator?


So, a few questions:

How does Creator know what to fill World with?
When does Creator do this?

:stylin: "Make games, not war."
:stylin: "Make games, not war.""...if you're doing this to learn then just study a modern C++ compiler's implementation." -snk_kid
Quote:
How does Creator know what to fill World with?

- it reads in the text file and follows commands. I've written all that and it works.
Quote:
When does Creator do this?
- When it's told to. Engine knows when it's time to start a new level and will tell World the level number and to update.





I think it is best if Creator is a nested class inside World, private so that Engine doesn't know about it. Engine will tell World to update itself, and won't know or care how it does that. When World is told to update, it will create an instance of its private nested Creator object, call its main function, then destroy it.

That all sounds simple enough. Every object only knows what it needs to know and does what it needs to do. The stuff that reads in the text file and executes its commands is all contained in Creator and therefore can be destroyed once it has done its job, ie before the gameplay begins for the current level.

So that is what I want to do, make Engine a nested class of World. I get the concept but will probably have a hard time implementing it as it sounds like it could be tricky syntax.

I didn't understand your examples above, with stuff like:

Creator( World& world ) : world_( world ) {};

But perhaps I don't need that; wasn't that when Creator was going to be created in World's constructor? Creator is going to be created in a member function of World called update(), then destroyed after it has done its work, something like this, I hope:

Creator* creator = new Creator();if ( creator->File(&this, level) ) //pass address of world, or maybe we don't need to?	delete creator;else	return;



Quote:Original post by darenking
So that is what I want to do, make Engine a nested class of World. I get the concept but will probably have a hard time implementing it as it sounds like it could be tricky syntax.

I'm going to assume you meant Creator instead of Engine ...

Quote:Creator is going to be created in a member function of World called update(), then destroyed after it has done its work, something like this, I hope:

*** Source Snippet Removed ***

If you're making Creator a nested class of World, it can reference static members, and any types or enumerations defined in World. For other members, access is granted in the usual way: an object, a pointer or a reference.

//bool Creator::File( World* ); // for passing a pointer to Worldbool Creator::File( World& ); // for passing a reference to World..void World::Update( unsigned int level ) {   Creator* creator = new Creator( level ); // construct with a level member:                                            // unsigned int Creator::level;// if( creator->File( this ) ) // pass our address ...      // World is filled   if( creator->File( *this ) )  // ... or we can pass us      // World is filled   delete creator; // do this no matter if Creator::File() is successful or not   return;};


EDIT: typos
:stylin: "Make games, not war.""...if you're doing this to learn then just study a modern C++ compiler's implementation." -snk_kid

This topic is closed to new replies.

Advertisement