• Advertisement
Sign in to follow this  

friend class

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

Hey I have a very large class called Gizmo. Now I have a very small class called Bomb. The Bomb class must be defined first because several of the Gizmo functions return pointers to Bombs and manipulate member variables of Bombs. Now suppose I don't ever want anyone to be able to manipulate the member variables of Bombs except for Gizmos? Like if a Gizmo sets the variables a certain way and returns a pointer to a Bomb, whoever recieves that pointer, I want them unable to change the data in the Bomb, I only want them to be able to read it. Well, I can make the Bomb variables private and add functions to return the variables, so now everyone can read the variables but no one can write to them. At first I thought I could make the Gizmo functions that need to write to Bomb variables friends of the Bomb class. But that doesn't work because Bomb has to be declared first. And to declare Gizmo functions friends of Bomb then Gizmo would have to be declared first. There must be a way to do what I want to do. But I don't know what it is. For now everything in Bomb is public.

Share this post


Link to post
Share on other sites
Advertisement
You can use a forward declaration when you specify a friend class in C++.

Share this post


Link to post
Share on other sites
It doesn't work.

/***************************************************************************
* Copyright (C) 2007 by icecubeflower *
* icecubeflower@yahoo.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/


//Gizmo.h
//defined it TWO files:
//Gizmo.cpp
//Gizmoswitch.cpp

#ifndef GIZMO
#define GIZMO

#include <fstream>
#include "TexBox.h"
#include "soundmaster.h"
#include "icegrid.h"
#include "icedistance.h"
#include "switchmaster.h"

using namespace std;

struct Gizmo;

struct Bomb
{
int GetDirection();
int GetForce();
friend bool Gizmo::Hitcheck(Bomb *kaboom);

private:
float fhor;
float fvert;
float fsize;
//0=player's team
//1=bad guy
//int iteam;
int idirection;
int iforce;
};

//considered making a GizStatBox structure/linked list that would store variables like base fspeed,
//idewey, texbox and just give
//Gizmos a pointer to it so only one GizStatBox node for all 100 rats or whatever. But for now
//every rat has own set of variables,
//the memory used is insiginificant compared to one map .png image so to hell with it for now
struct Gizmo
{
//in Gizmo.cpp
Gizmo();
~Gizmo();
//void Gizspawn(ImageVault *imagebox); //obsolete? maybe it goes with the editor, we'll see
bool Gizgen(string strgiz, float spawnhor, float spawnvert, ImageVault *imagebox);
// void Gizwrite();
// string texnamegrab(int inum);
// string texnamegrab2(int inum);
Iceblock Gizmove(int idirection, Uint32 history, Icematrix &mapgrid);
Iceblock Gizrun(int idirection, Uint32 history, Icematrix &mapgrid);
bool Hitcheck(Bomb *kaboom);
Gizmo* Hurt(float fpain,ImageVault *imagebox, AudioBox *boombox, float force, int idirection);
void Apocalypse();
void Gizface(int idirection);
Bomb* Attack(AudioBox *boombox);
void SubAttack(Bomb *kaboom, AudioBox *boombox);
GLuint GetPic();
GLuint GetPic(int inum);
int GetType();
void Grow();
void Shrink();
void Teleport(float x, float y, int idirection);
float GetHor();
float GetVert();
bool GetHurt();
float GetSize();
float GetSpin();
float GetHealth();
int GetState();
//in Gizmoswitch.cpp (this is the file you update if you add new Gizmo types)
Gizmo* gizupdate(Gizmo *enemy, int itrick, Icematrix &mapgrid, ImageVault *picbox,
AudioBox *boombox, Uint32 history);

private:
//in Gizmo.cpp //only used by Gizmove I think
Iceblock Gizsubmove(int idirection, float fmove, Icematrix &mapgrid);
void SlamSet(int idirection, float force);
Iceblock Gizslam(Uint32 history, Icematrix &mapgrid);
void Gizcalm(Icematrix &mapgrid, Uint32 history); //called by gizupdate()
void Die(AudioBox *boombox);
bool Hitcheck(Gizmo *killer);
//in Gizmoswitch.cpp
void Switcher(int inum);
Iceblock MoveAI(Iceblock icearray[], int ilen);

string name; //name of the gizmo file
float fhor;
float fvert;
float fspeed;
float basespeed;
float fsize; //radius in pixels (units)
//float basesize; //not actually using this yet
int itype; //type for AI and stuff
// Iceint spawntype; //probably only used in the editor, game engine probably stores this for no reason
int iturn; //used for AI, records how much time you want to pass before new command is issued
int iorder; //see the AI code
float fspin; //for windmills and stuff
float fhitpoints;
int ipics; //# of Gizmo pics
int idewey[6]; //see gizmoswitch.cpp
int imax;
GLuint iauto; //the image we're on

int itimer; //strictly used to time bhurt
float slamdistance;
float fupdate; //fupdate generally stores how many pixels Gizmo moved since last pic change and stuff
//could get rid of fupdate, fmoment, fpix, fspin, fspeed and make switches for itype, save memory or use
//GixStatBox but seems like overkill for now
float fpix; //tied to fupdate, the number of pixels you want this gizmo to move before you change sprites
float fmoment; //similar to fpix, time between attack frames
int igizstate; //see manual if i ever wrote it
bool bhurt; //is Gizmo invincible?

TexBox giztex;
};

#endif




It says:

cd '/home/icecube/project5/debug' && WANT_AUTOCONF_2_5="1" WANT_AUTOMAKE_1_6="1" gmake
gmake all-recursive
Making all in src
g++ -DHAVE_CONFIG_H -I. -I/home/icecube/project5/src -I.. -I/usr/include/SDL
-D_GNU_SOURCE=1 -D_REENTRANT -O0 -g3 -MT Gizbox.o -MD -MP -MF .deps/Gizbox.Tpo
c -o Gizbox.o /home/icecube/project5/src/Gizbox.cpp
In file included from /home/icecube/project5/src/Gizbox.h:34,
from /home/icecube/project5/src/Gizbox.cpp:21:
/home/icecube/project5/src/Gizmo.h:44: error: invalid use of incomplete type 'struct Gizmo'
/home/icecube/project5/src/Gizmo.h:38: error: forward declaration of 'struct Gizmo'
gmake[2]: *** [Gizbox.o] Error 1
gmake[1]: *** [all-recursive] Error 1
gmake: *** [all] Error 2
*** Exited with status: 2 ***



Share this post


Link to post
Share on other sites
Oh. I read this at
http://www.cplusplus.com/doc/tutorial/classes/

"The concepts of class and data structure are so similar that both keywords (struct and class) can be used in C++ to declare classes (i.e. structs can also have function members in C++, not only data members). The only difference between both is that members of classes declared with the keyword struct have public access by default, while members of classes declared with the keyword class have private access. For all other purposes both keywords are equivalent."

I guess when they said "the ONLY difference between both" and "For ALL other purposes both keywords are equivalent" they were exaggerating a little bit, huh?

Share this post


Link to post
Share on other sites
No, what I meant is that you can friend a class (or a struct) with just a forward declaration. You can't friend a member function of a class (or a struct) with just a forward declaration for the class (or struct).

Share this post


Link to post
Share on other sites
If I'm not mistaken you can't make methods friends, and even if you could, at this point the only thing that is known about Gizmo is that there exists something by that name.

Of course, why use the struct keyword in an unorthodox manner? :)

[edit]
Quote:

Hey I have a very large class called Gizmo. Now I have a very small class called Bomb. The Bomb class must be defined first because several of the Gizmo functions return pointers to Bombs and manipulate member variables of Bombs.


This is not really a reason why Bomb must be declared earlier. You could forward declare Bomb and separate the declaration and implementation of Gizmo. (A pretty usual procedure.)

Share this post


Link to post
Share on other sites
Oh. When I read about friendship it was making functions friends but it didn't say how to make a method in a structure a friend. So you guys are telling me I just make the entire structure a friend? That's... what I wanted to do anyway. Cool, I'll try that.

I don't know what you mean, separating the declaration and implementation of Gizmo, visitor. I have the prototype in a .h file and the rest in a .cpp file.

Share this post


Link to post
Share on other sites
Quote:

I have the prototype in a .h file and the rest in a .cpp file.


Then you already have them separated. Now forward declare Bomb in the header and include the respective header in the cpp file. The point is that the header of Gizmo is not accessing anything in Bomb and doesn't need to know more than it exists.

This tends to reduce dependencies between headers (if you change the header of Bomb, files including Gizmo but not Bomb won't need to be recompiled because the header of Gizmo hasn't changed as it doesn't depend on the header of Bomb) and is a good thing.

Share this post


Link to post
Share on other sites
Hey I didn't even need to do that forward declaration thing. All I had to do was say:

friend struct Gizmo;

in the Bomb struct and the compiler didn't care that Gizmo wasn't declared or defined or anything.

Share this post


Link to post
Share on other sites
It should be noted that resorting to "friend" is really a last-ditch effort sort of thing. Its there for when it makes sense, but the brief description you gave, perhaps because it was lacking in details, doesn't convince me that you're calling in "friend" out of necessity, rather than convenience.

It works, its fine, its not something you have to revisit now if you don't want. I'm just saying that "friend" is the strongest relationship you can express in C++, even stronger than inheritance, and as such, should be wielded wisely. Its a very powerful, and sometimes necessary, tool to be sure, but good engineering practice is to use the "weakest" tool that does the job adequately, because they have fewer negative side effects.

Share this post


Link to post
Share on other sites
I don't know, Ravyne. I use friend for Nodes in linked lists. I make everything in the Node private and make functions that can return values to everything. And then I make the Node a friend of the container. I think that makes sense.

And the Bomb above. ...well I guess I'd have to post a few pages of code to show what I'm doing. Not worth it. But Bomb is just a little struct. And basically I want nothing to be able to modify the contents of Bombs. Except Gizmos, which I want to be able to modify Bombs however they please. I think what I'm doing makes sense.

Share this post


Link to post
Share on other sites
Quote:
Original post by icecubeflower
Hey I didn't even need to do that forward declaration thing. All I had to do was say:

friend struct Gizmo;

in the Bomb struct and the compiler didn't care that Gizmo wasn't declared or defined or anything.


The statement "friend struct Gizmo;" is both a forward declaration of the class and a declaration of friendship. :)

Share this post


Link to post
Share on other sites
Quote:
Original post by icecubeflower
I don't know, Ravyne. I use friend for Nodes in linked lists. I make everything in the Node private and make functions that can return values to everything. And then I make the Node a friend of the container. I think that makes sense.

I may sound harsh on this but I my opinion whenever you use "friend" in your code you have build a design flaw into your code. Sure, there are some cases where it might be ok to use a friend but if you think you stumble across such a case you have to think twice and then rethink twice why you need this friend and how you can solve your actual problem by design rather than hacking an object access rules.

Basically a class is an object that has an interface. By public, protected, and private you defined who has access to which parts of the object's interface to mess with the object's internal states.

So far so good. But by using friend you actually allow another class to do whatever it wants with the object since it can not only access the whole interface but also change member attributes directly. That is not what object oriented programming was meant to be.

If you have a node class it should provide a public interface (a couple a methods) to let another class manage instances of the node class. The linked list should then be a class that can manage instances of a certain class type (or it could even be a template class) using the public interface of the class it does link together.

The node class must provide all public methods that are required for a manager class to deal with nodes. If you need to declare a friend as manager so that this manager can mess with the node's state you are constructing a weakness and a probable source of errors into your code. If you cannot implement the linked list class without being a friend of your node class than it is most likely that you node class' public interface lacks functionality it should provide when we are talking about object oriented programming.

Of course this is only my point of view, so no offense meant. But I always try to evangelize what I believe to be best practices of object oriented programming [grin]

Share this post


Link to post
Share on other sites
Quote:

I don't know, Ravyne. I use friend for Nodes in linked lists. I make everything in the Node private and make functions that can return values to everything. And then I make the Node a friend of the container. I think that makes sense.


You could do that, but if you look at something like std::list, you'd notice that having a Node would be simply completely useless for anything but std::list. std::list itself never exposes a Node to the user, so even if you could create and manipulate Node instances on your own and in any way you like, it would not affect any std::list.

E.g, this is from the GCC's std::list implementation.


namespace _GLIBCXX_STD
{
/// @if maint Common part of a node in the %list. @endif
struct _List_node_base
{
_List_node_base* _M_next; ///< Self-explanatory
_List_node_base* _M_prev; ///< Self-explanatory

static void
swap(_List_node_base& __x, _List_node_base& __y);

void
transfer(_List_node_base * const __first,
_List_node_base * const __last);

...
};

/// @if maint An actual node in the %list. @endif
template<typename _Tp>
struct _List_node : public _List_node_base
{
_Tp _M_data; ///< User's data.
};





Notice that it's all public. The things that should keep you away from it are identifiers beginning with underscore, the compiler-specific namespace - and the fact that these structs have no use for you.

Share this post


Link to post
Share on other sites
I don't care if people are harsh, I'll take all the info I can get.

Now that I think about it putting private member variables in a Node was pretty pointless. I mean I have a container class with a linked list of Nodes. Somewhere a container class is declared and you can only use the public functions of the container class. It doesn't matter if the Node is all public, the container class is the only thing that can get at it anyway. Because its *head and *tail and everything are private. (visitor just said the same thing I think, he posted while I was writing or maybe I didn't look at the 2nd page or something)

Share this post


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

  • Advertisement