[comp.lang.c++] protected friend classes?

rfg@riunite.ACA.MCC.COM (Ron Guilmette) (02/27/89)

I have a question and possibly a suggestion for a modification of the
language rules regarding friend declarations.

I have been writing some code in C++ and I
have now run across the same problem several times. It seems that
when I am building fancy linked data structures like queues, etc.
that I really need to use two classes; one to represent the "whole"
thing (e.g. tree, list, etc.) and another to represent the "things"
which are the nodes in the linked structure.  A simple example is
a singly linked list implementing a stack with just the normal push
and pop operations:

class plain_item {
	...
public:
	plain_item (plain_item *p);
};

class stackable_item : plain_item {
	stackable_item*	next;

	stackable_item (plain_item* p, stackable_item* new_next) : (p)
	{
		next = new_next;
	}

	stackable_item* get_next ()
	{
		return next;
	}

	friend class stack;
};

class stack {
	stackable_item*	top;
public:
	stack () { top = 0; };

	void push (plain_item* p)
	{
		top = new stackable_item (p, top);
	}

	plain_item* pop ()
	{
		if (!top)
			return NULL;
		else {
			plain_item* return_value = top;

			top = top->get_next();
			return return_value;
		}
	}
};

Notice that stackable_item has *no* public part.  That's good.  It only
needs to be known about by the class stack, which is its friend.

What bothers me about this is that all of the code for the all of the methods
of the class "stack" have far too much visibility into too many things that
they should not need to know (or be able to know) anything about.

Specifically, notice that the class stack can "see" not only the "next"
field of the stackable_item objects, but it can also "see" all of the
components of the "plain_item" part of each stackable_item.

How can I eliminate this unwanted and excessive visibility?  I know that
I can just eliminate the "friend class" declaration within stackable_item,
and make the two methods in stackable_item both public: (for all the
world to see!) but I don't want to do that either.  That also gives
excess visibility to things (i.e. the world would then have access to
the methods of stackable_item, but only the stack class should ever be
making references to these methods).

If I am right, and if this is a problem, I have a simple suggestion for
fixing this problem.

All that's needed is to declare that "A friend declaration within a class
declaration has one of three possible effects depending on which part of
the class it appears in.  Specifically, if a friend declaration appears
in the public part of a class, it has no effect.  If it appears in the
protected part of a class, then only the protected members of the containing
class are made visible to the designated friend.  Finally, if the friend
declaration appears in the private part of a class, then all members of
the class (public, protected, and private) are made visible to the designated
friend."

With this rule, I could now get the limited (minimal) visibility which
is needed, but re-writing the stackable_item class as follows:


class stackable_item : private plain_item {
	stackable_item*	next;

protected:

	stackable_item (plain_item* p, stackable_item* new_next) : (p)
	{
		next = new_next;
	}

	stackable_item* get_next ()
	{
		return next;
	}

	friend class stack;
};

Now even the one and only user of the stackable_item class (i.e. the stack
class) is forced to go through the (protected) interface to the
stackable_item class, and only the stack class can even "see" that!

Now that's what I want.  Can I have it please?

Oh yes.  One other point.  If the current language rules limited the
visibility of declared entities to their containing scopes, then I
would never have had this problem in the first place.  I could have
"nested" the declaration of the stackable_item class within the
stack class, and just made the methods for the stackable_item class
public.  Unfortunately, such nested declarations within classes "leak"
out.  I have always wondered why?  It just seems plain wrong.  Can anyone
tell me why such leakage is desriable and/or how it came to be a part of C++?
Honestly, it makes me ill just thinking about the "leakage".  With the
"leakage" rules of C++ in place, how am I supposed to achieve the complete
encapsulation of an abstract data type (which may need its own class
definitions) such as the simple stack I have shown?

-- 
// Ron Guilmette  -  MCC  -  Experimental (parallel) Systems Kit Project
// 3500 West Balcones Center Drive,  Austin, TX  78759  -  (512)338-3740
// ARPA: rfg@mcc.com
// UUCP: {rutgers,uunet,gatech,ames,pyramid}!cs.utexas.edu!pp!rfg

gjditchfield@watmsg.waterloo.edu (Glen Ditchfield) (02/28/89)

In article <99@riunite.ACA.MCC.COM> rfg@riunite.UUCP (Ron Guilmette) writes:
>It seems that when I am building fancy linked data structures like queues,
>etc.  that I really need to use two classes; one to represent the "whole"
>thing (e.g. tree, list, etc.) and another to represent the "things" which
>are the nodes in the linked structure.  A simple example is
[class plain_item;			to hold the data,
 class stackable_item : plain_item;	to add the link fields, and
 class stack;				which stacks plain_items.
    stackable_item declares stack to be a friend, to allow access to the
 link field.  But this gives stack _too_ much access to stackable_item and
 plain_item. ]

   When I do this, I derive plain_item from stack_item, so the visibility
problem isn't so severe.
   The next problem occurs when I try to derive a class from stack;
friendship isn't inherited, so the new class can't manipulate links.  A
protected function in stack is needed.  I don't know whether this is a big
problem, or if inheriting friendship would cause even bigger problems.

>[Suggests that the visibility granted to a friend should depend on
> whether the friend declaration appears in a private, protected or public
> section.]
>Oh yes.  One other point.  If the current language rules limited the
>visibility of declared entities to their containing scopes, then I
>would never have had this problem in the first place.  I could have
>"nested" the declaration of the stackable_item class within the
>stack class, and just made the methods for the stackable_item class
>public.  Unfortunately, such nested declarations within classes "leak"
>out.

   I'd rather see proper nested classes (static and non-static) added to
the language than have "friend" change.  A "friend" change would probably
break much existing code, but nested classes probably wouldn't.  Given the
recent addition of static member functions, static member classes seems
like an obvious idea.  I think every argument in favor of static member
functions applies equally to static member classes.
   For a taste of how nested classes can be used, look up "Block Structure
in Object Oriented Languages" in SIGPLAN Notices v21 #10 (oct '86) or "The
BETA Programming Language" in "Research Directions in Object-Oriented
Programming", Shriver & Wegner, eds.  Warning:  BETA is _different_.

    Glen Ditchfield  gjditchfield@violet.uwaterloo.ca  Office: DC 2517
Dept. of Computer Science, U of Waterloo, Waterloo, Ontario, Canada, N2L 3G1
	"... and the rest, we will spend foolishly!" -- _Svengali_