Path: utzoo!utgpu!news-server.csri.toronto.edu!rpi!zaphod.mps.ohio-state.edu!samsung!olivea!uunet!europa.asd.contel.com!neptune!gary From: gary@neptune.uucp (Gary Bisaga x4219) Newsgroups: comp.lang.c++ Subject: Re: About Lists and things... Message-ID: <1991Jun28.171252.32091@europa.asd.contel.com> Date: 28 Jun 91 17:12:52 GMT References: <28692A4A.59B7@tct.com> <1991Jun27.095856.2@minerva.inesc.pt> <1991Jun27.175256.3224@scrumpy@.bnr.ca> Sender: news@europa.asd.contel.com (News) Reply-To: gary@ctc.contel.com (Gary Bisaga x4219) Organization: Contel Technology Center Lines: 71 Nntp-Posting-Host: neptune.ctc.contel.com This is pretty long, you may want to hit 'n' now. In article <1991Jun27.175256.3224@scrumpy@.bnr.ca> bnrmtl!pat@larry.mcrcim.mcgill.edu writes: >In article <1991Jun27.095856.2@minerva.inesc.pt>, fmhv@minerva.inesc.pt (Fernando Manuel Vasconcelos) writes: >|> In article <28692A4A.59B7@tct.com> chip@tct.com (Chip Salzenberg) writes: >|> >|> >If you put a Circle into a ShapeList, you lose the Circle's >|> >compile-time type, because ShapeList.first() returns |Shape*|. >|> I may be missing the point, however consider two objections to your proposal: >|> >|> 1. A pratical one: You may not be able to change shape.h because it belongs >|> ... >|> >|> 2. A conceptual one: That means the interface of a base class depends on the > ... >Even if you define getRadius() for any Shape, if it only makes >sense for Circles, you're going to get code like the above. > >The style I prefer is to replace the entire if block with >a single call to a virtual function: > > this->doSomething(); I agree, but with the caveat that saying this is often easier than doing it. The problem in most of these messages (although it was alluded to in a couple) is that you need to consider what these "objects" really are. They really represent real-world things and should be treated accordingly. One need to approach the problem from an analysis point of view first, not a coding point of view. What is the real problem here? Well, in trying to make a Shape type with various sub-shapes under it, we are saying that each of those sub-shapes is a kind of Shape. So the interface to Shape must be in terms that the sub-shapes also understand. The problem here really is trying to extract an attribute ("radius") of a Shape, something that does not in the general case apply. I have seen the same kinds of problems come up when trying to start your development effort by saying: "Ok, I want to have a class called 'Object' at the top ..." You're starting with design rather than analysis. So, hopping down off the soapbox, what probably needs to be done is two things. First, in the case where you need to know how big the Shape is, return something that makes sense for a Shape in general. What is this? Well, not radius ... unless you define the "radius" of a Shape to be "The maximum distance taken up by the Shape in any direction" or something like that. Perhaps you need a new object type called Area or Region? In most cases, where you don't need an attribute returned, but you need some Real Work done, you should use member functions as much as possible, as indicated above. Of course, the situation where you're using a predefined library which defines a Shape in this manner is a slightly, but not necessarily hugely, different animal. First, you could always define a MyShape class that derives from Shape and just adds a virtual (pure?) GetRegion function. In some situations, that would make the most sense. In others, particularly the list example (my, we've strayed a long way from the original question, haven't we?) you could make List a member object of Shape (or MyShape). Heresy? No, not if you consider it from the analysis point of view. After all, inheritance describes an "is a" relationship. A question: Is a Shape a kind of List? No. But members describe a "has a" relationship. Does a Shape have a List? Well, sort of. Maybe a better question is: Does a newly-defined ListedShape object have a List? Does it have a Shape? Probably (hopefully) the answer is yes. Some cases may not fit any of these "ivory tower" situations. In those cases, almost anything seems to me preferable to putting virtual functions in the base class that don't make sense for some of most of the base's possible sub-classes. Just because it is good-looking C++ code (yeah, lots of virtual functions!) doesn't mean it's necessarily a good solution to the problem.