Article View: comp.std.c++
Article #2965Re: Friend & dual class heirarchies
From: John Max Skaller
Date: Sat, 30 Dec 1995 01:30
Date: Sat, 30 Dec 1995 01:30
151 lines
6051 bytes
6051 bytes
mfinney@inmind.com wrote:
>
>I keep running into the problem where I have dual class heirarchies
>and need to specify friend in one for the other.
> For example, suppose I have...
>
> struct BinaryNode {
> friend BinaryTree;
> friend AvlTree;
> ...};
>
> struct AvlNode {
> friend AvlTree;
> ...};
>
> struct BinaryTree {...};
> struct AvlTree : BinaryTree {...};
>
>If I add...
>
> struct RankedAvlNode : AvlNode {
> friend RankedAvlTree;
> ...};
>
> struct RankedAvlTree : AvlTree {...};
>
>then it is necessary to add new friend statements to BinaryNode
>and to AvlNode.
>
>The problem is that friend is designed to support those situations
>where classes are tightly coupled for some reason, such as a
>collection and its iterator. However, with the greater use of
>frameworks and design patterns, tightly coupled dual class
>heirarchies are becoming more and more common. .
>All the usual reasons for protected/private data members and
>functions apply ..
I guess the thing to do is be a bit suspicious of the design
technique. For example: why can the user see the node types
above at all?
An idea of how to re-engineer:
** Separate the interface and implementation **
The inteface classes don't need friend declarations
because there are no implementation details to be
friendly about :-)
The implementation classes can be structs, with public
data members, and no encapsulation at all. The interface
classes hide the details, but the implementation
classes can access other implementation classes without
friend declarations.
One method of doing this is to use indirection: a private
pointer to a struct. The user can pervert any
data they can lay their hands on .. but they can't
get their hands on it!
I have a similar kind of problem: I have several
kinds of invasive reference counting smart pointers: they
need access to a refcnt data member of the object
they manage. I make it public.
Now, this means the user can screw up the automatic
memory management. Is this bad?
I think it's the wrong question. The user has _asked_
a server (the smart pointer) to manage an object
_they_ created. Before that, they could already screw
up the memory management. By handing over to the smart
pointer, the user is asking for help -- the smart
pointer promises to help _provided_ the user doesn't
go and do something stupid (like manually delete the object).
But the user can still intervene and take control
back ... and may actually _need_ to fiddle the
reference count. For example .. by inventing
another kind of reference counting smart pointer!
With several kinds of smart pointers
the user may decide to change the management protocol
and may need to manually adjust the ref count
in the process of transfering responsibility.
The refcount could be made private by making the
smart pointer a friend. But then only the nominated
kind of smart pointer can manage the object.
I HAVE caused a crash by messing up invasive
smart pointers. But I didn't do it by writing
to the publically accessible refcount, I did it
by stealing the raw pointer and giving it
to a _non-invasive_ reference counted pointer --
which thought it had exlusive control and deleted
the object from under the nose of the counting
smart pointers.
In my opinion, encapsulation of objects is not quite right:
data structures and functions in an implemtation
module should have free access to other components
of the module. [Naturally, the user interface provides
only managed access].
That is, C++ and other "object" oriented systems
operate by determining that the granularity
of the module notion is the class -- which is too
fine. C++ provides friends to escape this design
compromise (which seems necessary for linear
polymorphism).
This suggests working with structs and global functions
in a space without any protection, (simulating
a module of coarser granularity) and then hiding
the implementation of the _system_ behind an
interface -- which may look nothing like the
representation.
If you consider STL, it is very much an unprotected,
unencapsulated system residing in the implementation domain.
To use it, it needs to be hidden
inside encapsulated objects of the problem domain.
So it may pay to be a bit sloppier about protection
of data structures .. since they're largely tools
for creating representations of abstractions,
and it is _these_ representations that need to be hidden,
rather than the details of the tools used to create them.
For example: if you have objects that should NOT be moved,
then you might put them in a list<>, which promises never to
move things. You would frown on a vector<>, because it
does tend to move things.
..tend to. But sometimes, you can reserve storage
in the vector, and then objects won't move. You can
rely on it if you know the implementation details.
--
John Max Skaller voice: 61-2-566-2189
81 Glebe Point Rd fax: 61-2-660-0850
GLEBE NSW 2037 web: http://www.maxtal.com.au/~skaller/
AUSTRALIA email: skaller@maxtal.com.au
---
[ comp.std.c++ is moderated. Submission address: std-c++@ncar.ucar.edu.
Contact address: std-c++-request@ncar.ucar.edu. The moderation policy
is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
Technical Details
Message-ID:
<4c17cf$k52@oznet03.ozemail.com.au>Path:
arc1-ca.pugleaf.net!in2.uu.net!bounce-backReferences:
<4bddll$8hg@mujibur.inmind.com>Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!in2.uu.net!bounce-back Date: 30 Dec 95 01:30:42 GMT Approved: fjh@cs.mu.oz.au From: John Max Skaller <maxtal@suphys.physics.su.oz.au> Newsgroups: comp.std.c++ Subject: Re: Friend & dual class heirarchies X-Original-Date: 29 Dec 1995 17:09:35 GMT Organization: MAXTAL Message-ID: <4c17cf$k52@oznet03.ozemail.com.au> References: <4bddll$8hg@mujibur.inmind.com> X-Mailer: Mozilla 1.2N (Windows; I; 16bit) X-Auth: PGPMoose V1.1 PGP comp.std.c++ iQBFAgUBMOSWU+EDnX0m9pzZAQEQ6QF+P7uJa+BikrkESbFgrZ8aogNtNB25kBIT V5AkYrPiZeIwPKGo614dXc1pX67wrZXh =p5qc