psrc@pegasus.ATT.COM (Paul S. R. Chisholm) (03/27/90)
(I tried to be careful about attribution; I apologize if I goofed.) In article <169@pollux.kulcs.uucp>, herman@kulcs.uucp (Herman Moons) writes: > I don't know whether this question appeared earlier on the net. (*sigh*) I don't suppose anyone's collecting frequently asked questions (and their answers) . . . Also, please note that this whole discussion deals only with the abstract data typing features of C++, not the object-oriented features. > Are there any *good* reasons for having the friend concept in C++ ? In article <10589@alice.UUCP> ark@alice.UUCP (Andrew Koenig) writes: > Sure. One of the most common is when you're writing several > interrelated classes. In article <1082@targon.UUCP>, ruud@targon.UUCP (Ruud Harmsen) writes: > Only too true. And isn't this also the fundamental weakness of the > object oriented concept? For in the real world, and so in any > realistic information system describing a part of that real world, > *nearly all* classes are interrelated to *nearly all* other > classes. Classes often only make sense just because of their > relations to other classes. So if you have to use friends for that, > everyone will be anybody's friend, and as a result of all this > friendship, a *real* project in an OO-language tends to become just > the same mess as it does in a traditional language, like C. As Dr. Koenig pointed out in a later article, "interrelated" means "the implementation of one thing depends on the *implementation* of another thing". A "thing" in this case may be a whole class. If you're careful, only a few functions in class B (e.g., member function foo and static member function bar) will need to know about the implementation of class A; in which case, only B::foo and B::bar will need be friends of A. This is a Good Thing; it specifically limits what software can mess with an A, and what code must be looked at when the implementation of A changes. There's still a limitation, in that B::foo and B::bar must be visible to A to be friends of A. That implies that one class's private functions can only be friends of another class if the functions are public (or possibly protected), or if the whole first class is a friend of the second class. (I *assume* the visibility rules apply to friendship.) In article <626@ksr.UUCP>, reg@ksr.UUCP (Rick Genter) writes: > As one who does have experience with OOT and with complicated > applications, I (unfortunately) have to agree with the gist of this > assessment. . . . > Note that these [hard applications] can be made to work, but at a > significant performance penalty. The penalty is not the penalty of > a single virtual method invocation vs. a single function call; I > grant that that is (usually) insignficant. But often what happens > is that a method will invoke several other methods, each of which > will invoke other methods, and so on and so on. Suddenly that > insignificant overhead becomes quite significant. This has nothing to do with object-oriented design or programming; it has to do with data abstraction, and with structuring software into layers. (I assume that you're talking about function call overhead per se, and not function invocation vs. virutual method invocation. The difference is still trivial, especially if you add in the comparisons that would otherwise need to be made.) Granted, almost all object- oriented designs will have layering; but so will almost all non-trivial designs, OO or otherwise. One of the highlights of C++ is that it allows trivial layers to have trivial cost (i.e., a layer that just invokes another layer can be implemented with an inline function). > It seems to me that a study needs to be performed not on how to make > everything OO, but on how to make composite designs work: i.e., how > do I easily integrate my OO UI with my procedural data-flow > analysis. What, ho! "Composite design" is a collection of guidelines to follow when designing software: to maximize the strength (or "cohesion") of modules, and to minimize the coupling between modules. These principles are as applicable to OOD as any other design approach, and I strongly recommend all of the following references: L. L. Constantine, G. J. Meyers, and W. P. Stevens, "Structured Design", IBM SYTEMS JOURNAL, vol. 13, no. 2, pp. 115-139. Glenford J. Myers, RELIABLE SOFTWARE THROUGH COMPOSITE DESIGN, New York: Petrocelli/Charter, 1975. Edward Yourdon and Larry L. Constantine, STRUCTURED DESIGN: FUNDAMENTALS OF A DISCIPLINE OF COMPUTER PROGRAM AND SYSTEMS DESIGN, Englewood Cliffs, N.J.: Prentice-Hall, 1979. The last is in some sense the most complete. It also describes what has since been known as "structured design", or the Yourdon half of the Yourdon/DeMarco methodology that underlies almost all CASE tools. One starts by drawing dataflow diagrams; then one picks the "central transform" in the diagram, making that the root of a tree, and mechanically transforming the diagram into a tree. (The result, according to a comment from my CASE instructor, does *not* produce designs that have high cohesion and low coupling; but I have no reference other than this heresay.) While the method sounds reasonable, I've never been able to non-trivially decompose an interactive application into a dataflow diagram. Thus, Rick's comment about dataflow diagrams and OO user interfaces baffles me. Paul S. R. Chisholm, AT&T Bell Laboratories att!pegasus!psrc, psrc@pegasus.att.com, AT&T Mail !psrchisholm I'm not speaking for the company, I'm just speaking my mind.
dog@cbnewsl.ATT.COM (edward.n.schiebel) (03/27/90)
From article <4568@pegasus.ATT.COM>, by psrc@pegasus.ATT.COM (Paul S. R. Chisholm): > A "thing" in this case may be a whole class. If you're careful, only a > few functions in class B (e.g., member function foo and static member > function bar) will need to know about the implementation of class A; in > which case, only B::foo and B::bar will need be friends of A. This is > a Good Thing; it specifically limits what software can mess with an A, > and what code must be looked at when the implementation of A changes. > > There's still a limitation, in that B::foo and B::bar must be visible > to A to be friends of A. That implies that one class's private > functions can only be friends of another class if the functions are > public (or possibly protected), or if the whole first class is a friend > of the second class. (I *assume* the visibility rules apply to > friendship.) > Not quite. A private member function of B cannot be called by an A, but class A may make a private member of B a friend: B's foo() may access private parts of A, but A cannot call B::foo(). class B { public: B(); private: void B::foo(); } class A { public: A(); friend void B::foo(); }; Ed Schiebel AT&T Bell Laboratories dog@cblph.att.com
ruud@targon.UUCP (Ruud Harmsen) (03/29/90)
In article <4568@pegasus.ATT.COM> psrc@pegasus.ATT.COM (Paul S. R. Chisholm) writes: >[...] >In article <1082@targon.UUCP>, ruud@targon.UUCP (Ruud Harmsen) writes: >> Only too true. And isn't this also the fundamental weakness of the >> object oriented concept? For in the real world, and so in any >> realistic information system describing a part of that real world, >> *nearly all* classes are interrelated to *nearly all* other >> classes. >> [...] >As Dr. Koenig pointed out in a later article, "interrelated" means "the >implementation of one thing depends on the *implementation* of another >thing". If the knowledge of the "implementation" of a thing includes to know names and types of the elements in a structure, it is my experience that some 70 to 80 percent of the code deals with several elements from several structures, in many different combinations. If then you try to restrict direct access to the structure elements to functions of the same class, while providing access from outside the class through functions, you may have to create an individual function for every element. This gives me the feeling that calling such a function becomes just a syntactic variant of the "." and "->" operators, and only obscure things, and help in no way whatever. >A "thing" in this case may be a whole class. If you're careful, only a >few functions in class B (e.g., member function foo and static member >function bar) will need to know about the implementation of class A; in >which case, only B::foo and B::bar will need be friends of A. This is >a Good Thing; it specifically limits what software can mess with an A, >and what code must be looked at when the implementation of A changes. > [...] In theory, this is correct. But if in practice, in a particular application 70 to 80 percent *has* to mess with a class A, then you can't do very much limiting. I think smart cross-reference tools, that tell you where the messing is done, and also how (read-only, or write-also) it is done, are much more useful, and can be applied to traditional languages just as well.
ruud@targon.UUCP (Ruud Harmsen) (04/05/90)
In article <4593@pegasus.ATT.COM> psrc@pegasus.ATT.COM (Paul S. R. Chisholm) writes: >I wrote: >> >A "thing" in this case may be a whole class. If you're careful, only a >> >few functions in class B (e.g., member function foo and static member >> >function bar) will need to know about the implementation of class A; in >> >which case, only B::foo and B::bar will need be friends of A. This is >> >a Good Thing; it specifically limits what software can mess with an A, >> >and what code must be looked at when the implementation of A changes. > I wrote: >> In theory, this is correct. But if in practice, in a particular >> application 70 to 80 percent *has* to mess with a class A, then you can't >> do very much limiting. > >Exactly right. That's the signature of a poor design. You have to >*try*, hard, to hide the implementation of your data types. Twenty >years of programming have taught me that it's worth the effort. Lots >of other believe it, too. I really hope you, and the other believers, are right. But I'm not convinced. I don't think the trouble is in bad design, I think it's in the nature of things. The world simply isn't simple, cannot be forced into strict hierarchical models (remember hierachical databases, how much trouble they cause(d) in designing a system?), and any design method that ignores or simplifies these facts, will eventually fail. But I am always willing to learn, and I will keep on trying to apply the theories, because I do believe the principles you mention are themselves useful, and will help a bit, only not as much as many people seem to think. >I'm not speaking for the company, I'm just speaking my mind. So am I. Ruud Harmsen.
ruud@targon.UUCP (Ruud Harmsen) (04/05/90)
In article <4593@pegasus.ATT.COM> psrc@pegasus.ATT.COM (Paul S. R. Chisholm) writes: >> In theory, this is correct. But if in practice, in a particular >> application 70 to 80 percent *has* to mess with a class A, then you can't >> do very much limiting. > I wrote: >Exactly right. That's the signature of a poor design. You have to >*try*, hard, to hide the implementation of your data types. Twenty >years of programming have taught me that it's worth the effort. Lots >of other believe it, too. Perhaps it all depends a lot on the kind of application. In the ones I dealt with most (financial accounting, invoicing, banking etc.) the implementation of the data types, say the fields of the structs, are exactly what the *user* of the system deals with, viz. enters, browses, prints, talks about with his clients, etc. How could it be useful to *hide* these things, if to the *user* of the system, they are the only things that matter? If you hide the data-items, nothing useful remains. I can imagine that in other sorts of applications, perhaps an operating system, or a graphics system, this is quite different. For example, the UNIX implementation of buffered IO is built in an OO-fashion, and I am very content that I don't have to know what exactly hides behind the type FILE, to be able to use it. But now tell a bookkeeper he doesn't need to know what data items are in the system about his debtors! So maybe that's why we may well be both right, and simply talking ahead of each other, because we simply don't have the same kind of systems in mind?
davidm@uunet.UU.NET (David S. Masterson) (04/10/90)
In article <1115@targon.UUCP> ruud@targon.UUCP (Ruud Harmsen) writes:
Perhaps it all depends a lot on the kind of application. In the ones
I dealt with most (financial accounting, invoicing, banking etc.) the
implementation of the data types, say the fields of the structs, are
exactly what the *user* of the system deals with, viz. enters, browses,
prints, talks about with his clients, etc.
How could it be useful to *hide* these things, if to the *user* of the
system, they are the only things that matter? If you hide the data-items,
nothing useful remains.
Even in these types of applications, there is more to be concerned with than
just the structure of the data. There may also be rules that go with the
data. For instance, updating an employee's record may invoke a rule against
the company's financial records. This may not be hidden from the user (or it
may), but the business rule must occur and, so, should be "tied" to the
employees data structure. Sometimes (oftentimes), it is useful to hide this
tie from the "ordinary" user who is not allowed to change the rule anyway.
--
===================================================================
David Masterson Consilium, Inc.
uunet!cimshop!davidm Mt. View, CA 94043
===================================================================
"If someone thinks they know what I said, then I didn't say it!"
lucio@proxima.UUCP (Lucio de Re) (04/10/90)
In article <1114@targon.UUCP> ruud@targon.UUCP (Ruud Harmsen) writes: >In article <4593@pegasus.ATT.COM> psrc@pegasus.ATT.COM (Paul S. R. Chisholm) writes: >I wrote: >>> In theory, this is correct. But if in practice, in a particular >>> application 70 to 80 percent *has* to mess with a class A, then you can't >>> do very much limiting. >> >>Exactly right. That's the signature of a poor design. You have to >>*try*, hard, to hide the implementation of your data types. Twenty >>years of programming have taught me that it's worth the effort. Lots >>of other believe it, too. I am one of those believers. Fortunately for us programmers, the reality we have to contend with is seldom of such a nature that abstraction cannot reduce it to the point where classes provide an adequate description. Even Modula 2, without the object oriented abstractions did a formidable job of data hiding and at the same time simplified coding. I appreciate that it is difficult to isolate individual classes at a glance in a great many instances, and I presume no proof is available to demonstrate that a class concept is general enough to cover all instances of programmable tasks (wow, what a mouthful!); I also think that Paul Chisholm used excessively strong terminology in "the signature of a poor design," but his point is valid, better design will produce one or more classes more faithfully modelling the system underscrutiny (I must stop this pompousness, please no flames!). Remember that inheritance, and particularly multiple inheritance, with its greater complexity, ought to extend the power of object oriented design to (hopefully) all programmable instances. It probably doesn't, as there is an infinite amount of complexity out there and (I guess) a finite number of constructs programmers can design, but somebody better versed in mathematical theory than I should attempt a proof in either direction while we programmers continue with our heuristic approach to problem solving. >I really hope you, and the other believers, are right. But I'm not >convinced. I don't think the trouble is in bad design, I think it's >in the nature of things. The world simply isn't simple, cannot be >forced into strict hierarchical models ... I suppose the proof of the pudding ... I contend that (a) you are correct in believing that hierarchies are not sufficient to represent all possible conditions, but (b) the problems that programmers face in (say) 90% of instances have solutions that can be decomposed into hierarchical representations, and (c) that careful analysis and possi- bly a fair amount of thumb-sucking guesswork can bring whatever the initial percentage might be a lot closer to a 100% of all USEFUL applications. My fear, and perhaps it is Paul's fear as well, is that because the object-oriented model is not always immediately obvious, the program- mer may overlook it and use a (currently) more traditional approach. I suggest that when a problem that looks as if it does not lend itself to decomposition into object oriented classes occurs, it should be submitted for scrutiny on this forum. I believe that the field is young enough for all of us to have something to learn from the experiences of others, while at the same time the presenter should weigh the (possible) embarrassment of having the obvious pointed out to him/her (and I do not exclude the possibility that the forum may not succeed in the quest for a suitable representation, to the embarrassment of the forum in its entirity) against the possibility that an elegant solution may simplify immediate programming (unlikely as we all have deadlines to work to), and certainly future maintenance. ---------------------------------------------------------------------- I used to design nuclear reactors and you should see the code that engineers and scientists come up with. Yuuuck! Spaghetti everywhere. -------------------------------------------------- (Kenneth L Moore) - Lucio de Re ...uunet!ddsw1!olsa99!proxima!lucio -------------------------------------------------------- lucio@proxima Disclaimer: He must have been joking!
lucio@proxima.UUCP (Lucio de Re) (04/10/90)
In article <1115@targon.UUCP> ruud@targon.UUCP (Ruud Harmsen) writes: >Perhaps it all depends a lot on the kind of application. In the ones >I dealt with most (financial accounting, invoicing, banking etc.) the >implementation of the data types, say the fields of the structs, are >exactly what the *user* of the system deals with, viz. enters, browses, >prints, talks about with his clients, etc. > >How could it be useful to *hide* these things, if to the *user* of the >system, they are the only things that matter? If you hide the data-items, >nothing useful remains. I think there is some confusion here. I presume (and I'm a bit of a novice at this, so please be tolerant) that, whereas the *user*does* want to see the fields separately, they are grouped at all times and are very seldom manipulated out of context. Nobody contends that the fields are not disjoint, but, in APL parlance, changing one element of a matrix affects the matrix as a whole. Likewise, changing the balance on a debtor record affects the entire record. Let's take one example: Let's say that the internal representation of a debtor consists of a master file record with debit and credit balances, current and prior to the present, as well as the (active) history in the form of transactions. It is arbitrary whether one wants the occurrence of a new transaction to affect the debtor's balance in the master file record or not, but it is not arbitrary that the actual debtor's balance _is_ affected. Good design dictates that the representation of the debtor in the computer system should be such that the master file record is tightly coupled to the transaction records, so that they are viewed by the system as a single entity. That is how I perceive OOP to function, not as a limiting factor that forces all objects to be lumped together, but rather providing a unification glue so that aspects that belong together are always kept that way. Sorry to soapbox like this, I hope I haven't muddled the concepts beyond understanding. ---------------------------------------------------------------------- I used to design nuclear reactors and you should see the code that engineers and scientists come up with. Yuuuck! Spaghetti everywhere. -------------------------------------------------- (Kenneth L Moore) - Lucio de Re ...uunet!ddsw1!olsa99!proxima!lucio -------------------------------------------------------- lucio@proxima Disclaimer: He must have been joking!