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...