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_