[comp.lang.c++] Inheritance and Information Hiding

chip@tct.uucp (Chip Salzenberg) (01/28/91)

According to tynor@hydra.gatech.edu (Steve Tynor):
>In practice, I've found C++ `private's to be overly restrictive and in
>violation of the rule "it is not the buisiness for a class to decide how
>it may be extended in the future" (paraphrased from OOSC).

An interesting "rule" -- one with which I happen to disagree.

In any case, language features don't stand up and say, "Use me!"  Who
can say that there will never be good reason to make members private?
Not I -- even though I use "protected" five times more often than I
use "private".

>It's another example of a C++ feature that actually reduces the reusability
>of C++ classes (non-virtual member functions being the other obvious one).

Virtual functions are available when appropriate; but I'm just as glad
that I need not pay the performance penalty when I don't need them.
-- 
Chip Salzenberg at Teltronics/TCT     <chip@tct.uucp>, <uunet!pdn!tct!chip>
       "If Usenet exists, then what is its mailing address?"  -- me
             "c/o The Daily Planet, Metropolis."  -- Jeff Daiell

linton@sgi.com (Mark Linton) (01/30/91)

In article <27A44871.5586@tct.uucp>, chip@tct.uucp (Chip Salzenberg) writes:
|> According to tynor@hydra.gatech.edu (Steve Tynor):
|> >In practice, I've found C++ `private's to be overly restrictive and in
|> >violation of the rule "it is not the buisiness for a class to decide how
|> >it may be extended in the future" (paraphrased from OOSC).
|> 
|> An interesting "rule" -- one with which I happen to disagree.
|> 
|> In any case, language features don't stand up and say, "Use me!"  Who
|> can say that there will never be good reason to make members private?
|> Not I -- even though I use "protected" five times more often than I
|> use "private".
|> 
|> >It's another example of a C++ feature that actually reduces the reusability
|> >of C++ classes (non-virtual member functions being the other obvious one).
|> 
|> Virtual functions are available when appropriate; but I'm just as glad
|> that I need not pay the performance penalty when I don't need them.

Private is for things that might change.  If you make them protected and
then change them, you break subclasses.  So if you're not willing to commit
to keeping something around, then you should make it private.  After starting
with the attitude that I should make things by default protected, I now believe
the appropriate default is private.  If I make something available to a subclass,
I should think carefully about what I am giving out.

As for virtual functions, if you can't live with cost of a virtual function call
you are unlikely to be happy with the cost of a direct function call.  A virtual
function call typically adds a few memory references on top of what is likely
a considerably more expensive operation, involving register save/restore and
a branch.

jbuck@galileo.berkeley.edu (Joe Buck) (01/31/91)

According to tynor@hydra.gatech.edu (Steve Tynor):
> >In practice, I've found C++ `private's to be overly restrictive and in
> >violation of the rule "it is not the buisiness for a class to decide how
> >it may be extended in the future" (paraphrased from OOSC).

In article <27A44871.5586@tct.uucp>, chip@tct.uucp (Chip Salzenberg) writes:
> An interesting "rule" -- one with which I happen to disagree.

I also disagree.  There is a tension between information hiding and
inheritance, and both are valuable concepts.

The ability to reuse code is very valuable, but the ability to
separate interface and implementation, and to be able to, in many
cases, redesign the implementation without affecting the user of
a class is extremely valuable.

But what the advocates of never using "private" are forgetting is
that the person that derives from a class is also a user of the
class -- one that is more privileged than the average client, but
a client nevertheless.

Chip goes on to write:
> In any case, language features don't stand up and say, "Use me!"  Who
> can say that there will never be good reason to make members private?
> Not I -- even though I use "protected" five times more often than I
> use "private".

"private" is very valuable for enforcing invariants: for example, assuring
that certain data members will always have a certain relationship.  For
example, consider a class of objects that represent two-way connections.
The base class has only one data member: a pointer to the object that is
connected to it (a null pointer will represent no connection).  We want
derived classes to be able to manipulate the connection however they please,
with this proviso: if A is connected to B, B is connected to A.  If A
is deleted, B will revert to being disconnected (we will never have
a dangling pointer to a deleted object).

Now, suppose we want derived classes to be able to control the type of
object that is connected to it: a DerivedConnection can only be connected
to another DerivedConnection.  This means we don't want the raw connect
methods to be generally accessible: we make the methods protected, so
derived classes can build on them however they like.

Here is a subset of the interface:

class Connection {
private:
	Connection * peer;		// who I am connected to
protected:
	// disconnect me if I am connected
	void disconnect() {	
		if (peer) peer->peer = 0;
		peer = 0;
	}
	// form a new connection
	void connect(Connection& newPeer) { 
		disconnect();
		newPeer.disconnect();
		peer = &newPeer;
		newPeer.peer = this;
	}
	// return who I am connected to
	Connection * myPeer() const { return peer;}
public:
	Connection() : peer(0) {}
	~Connection() { disconnect();}
}

A real implementation will have additional functions; myPeer might be
public.

> >It's another example of a C++ feature that actually reduces the reusability
> >of C++ classes (non-virtual member functions being the other obvious one).

Reusability is not the only thing we care about.  Those of us building
large systems also care about robustness.  Notice that the above code
(modulo any bugs or stupidity; I haven't checked it) enforces several
assertions about connections that I can count on: these assertions can't
be broken by derived classes OR by clients.  Only two methods can alter
a "peer" pointer.

> Virtual functions are available when appropriate; but I'm just as glad
> that I need not pay the performance penalty when I don't need them.

Agreed.  Imagine the performance drag if every function in a complex
number class were virtual.

C++ is not Smalltalk, and object-oriented programming is not Smalltalk
programming.  Ready Grady Booch's book for a lot more detail on the
private/protected/public distinction.

|> Chip Salzenberg at Teltronics/TCT     <chip@tct.uucp>, <uunet!pdn!tct!chip>
|>        "If Usenet exists, then what is its mailing address?"  -- me
|>              "c/o The Daily Planet, Metropolis."  -- Jeff Daiell

--
Joe Buck
jbuck@galileo.berkeley.edu	 {uunet,ucbvax}!galileo.berkeley.edu!jbuck	

jbuck@galileo.berkeley.edu (Joe Buck) (01/31/91)

In article <1991Jan29.221121.20642@odin.corp.sgi.com>, linton@sgi.com (Mark Linton) writes:
> As for virtual functions, if you can't live with cost of a virtual function call
> you are unlikely to be happy with the cost of a direct function call.  A virtual
> function call typically adds a few memory references on top of what is likely
> a considerably more expensive operation, involving register save/restore and
> a branch.

The choice isn't always between the overhead of a virtual function call and a direct
function call -- you forgot about "inline".  In most cases virtual functions can't
be inlined (except in cases where the compiler knows the exact class).

--
Joe Buck
jbuck@galileo.berkeley.edu	 {uunet,ucbvax}!galileo.berkeley.edu!jbuck	

mat@mole-end.UUCP (Mark A Terribile) (02/03/91)

> |> >In practice, I've found C++ `private's to be overly restrictive and in
> |> >violation of the rule "it is not the buisiness for a class to decide how
> |> >it may be extended in the future" (paraphrased from OOSC).

> |> >It's another example of a C++ feature that actually reduces the
> |> >reusability of C++ classes ....

> Private is for things that might change.  If you make them protected and
> then change them, you break subclasses.  So if you're not willing to commit
> to keeping something around, then you should make it private.  ...  I now
> believe the appropriate default is private.  If I make something available
> to a subclass, I should think carefully about what I am giving out.

How chummy you should be with your DERIVED CLASSES (`subclass' is a word that
belongs to another language and to certain theories of programming) depends,
I think, on what your relationship to them is.  If, as an engineering reality,
the base class and its derived classes are written and maintained as a unit,
and if the inheritance expresses a cuddling-close relationship, then there's
no reason why the data layout and data-layout dependencies shouldn't be
visible to the derived classes.

In other cases, it's probably a poor idea.  Note, *PROBABLY*.  Where the
base class's data layout is a matter of record (perhaps it maps some
data layout that is externally imposed, or that is essentially immutable in
the face of the algorithms/mechanisms which the code is DEFINED to implement)
it might make sense to make the data layout a part of the class's interface.

We have to remember that we are solving problems.  Such things as indentation
and brace layout can be argued about without taking the problem into account.
The same cannot always be said about how the parts of the program are willing
to relate to each other.
-- 

 (This man's opinions are his own.)
 From mole-end				Mark Terribile

chip@tct.uucp (Chip Salzenberg) (02/08/91)

According to rick@tetrauk.UUCP (Rick Jones):
>In article <27A9AB0C.4794@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:
>>Inheritance and information hiding issues are orthagonal to assertion
>>issues.  A given language may support the former, the latter, both, or
>>neither.  Let's keep the discussions clear.
>
>The connection between assertion and information hiding
>had already been made.

Okay, I'll grant that point.

My reaction was perhaps stronger than necessary because I don't want
this discussion to degenerate into "my language can beat up your
language."  I'm sorry if I overdid it.

>My point is that, as far as maintaining invariants is concerned, there is more
>than one way to skin a cat.  In fact my view is that assertions are a superior
>method.

I can see a place for both.  There are times when strict invariant
checking would have caught problems in my code.

The GNU C++ compiler includes a "wrapper" mechanism, which replaces
calls to functions with specific return and parameter types with calls
to a wrapper function, one parameter of which is the address of the
"real" function.  This mechanism is too limited for general assertion
checking, however, because of the requirement that each set of return
and parameter types have its own wrapper.

I read about a proposed extension to C++ called A++, or Annotated C++,
by Marshall Cline of Clarkson University and Doug Lea of SUNY.  It
defines assertions for the class ("legal:") and for member functions
("axioms:" with "require" and "promise" assertions).

The resemblance of A++'s assertions to those of Eiffel is acknowledged
in the introduction to the paper, ``Using Annotated C++.''  (Similar
problems lead to similar solutions.)  They hoped to avoid redundant
checks when member functions call each other, and described obvious
code generation tricks to that end.

I wonder if they ever got their proof-of-concept compiler working...