home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #18 / NN_1992_18.iso / spool / comp / lang / cplus / 12382 < prev    next >
Encoding:
Text File  |  1992-08-15  |  7.0 KB  |  184 lines

  1. Newsgroups: comp.lang.c++
  2. Path: sparky!uunet!munnari.oz.au!metro!extro.ucc.su.OZ.AU!maxtal
  3. From: maxtal@extro.ucc.su.OZ.AU (John MAX Skaller)
  4. Subject: Re: Downcasting (was: Re: run-time type checking)
  5. Message-ID: <1992Aug15.170141.14667@ucc.su.OZ.AU>
  6. Sender: news@ucc.su.OZ.AU
  7. Nntp-Posting-Host: extro.ucc.su.oz.au
  8. Organization: MAXTAL P/L C/- University Computing Centre, Sydney
  9. References: <9222715.29197@mulga.cs.mu.OZ.AU> <4823@holden.lulea.trab.se>
  10. Date: Sat, 15 Aug 1992 17:01:41 GMT
  11. Lines: 171
  12.  
  13. In article <4823@holden.lulea.trab.se> jbn@lulea.trab.se (Johan Bengtsson) writes:
  14. >fjh@munta.cs.mu.OZ.AU (Fergus James HENDERSON) writes:
  15. >: jbn@lulea.trab.se (Johan Bengtsson) writes:
  16. >: 
  17. >: >maxtal@extro.ucc.su.OZ.AU (John MAX Skaller) writes:
  18. >: >:
  19. >: >:    class Base { //....
  20. >:         virtual operator Derived1*() { return 0; }
  21. >:         virtual operator Derived2*() { return 0; }
  22. >: >:    };
  23. >: >:Now what does this do? Well, it preserved th open/closed principle
  24. >: >:and encapsulation and the explicit interfaces principle.
  25. >: >
  26. >: >What if someone using your classes decides to add a Derived3 class
  27. >: >to which downcasting should be possible?  He must modifiy your
  28. >: >class Base, for which he may not have source code.
  29. >: 
  30. >: Yes, but that would be a change to the interface!
  31. >
  32. >    I get your point.  I also agree that the above technique
  33. >    is best (most efficient) when you have a limited set
  34. >    of known derived classes.
  35.  
  36.     No one here is arguing about efficiency.
  37.     The set of derived classes is ALWAYS finite in a domestic
  38.     program.
  39.  
  40.     The word 'known' is the keyword.
  41. >
  42. >: 
  43. >: The whole point is that the ability to downcast a Base to a derived class
  44. >: SHOULD be part of the interface. If you want to change the interface, then
  45. >: of course you are going to need to modify the source code - this does not
  46. >: violate the open/closed principle.
  47. >
  48. >    I agree that unrestricted typecasting provides the ability to
  49. >    cleanly extend the interface of a base class, into the interface
  50. >    of any derived class.  Although I admit that in a way this does
  51. >    break the "closedness" of the Base class interface, I don't think
  52. >    it does so in a "bad" way.
  53.  
  54.     This is the issue. You might be right, but I'm not convinced.
  55. In a statically secure system, I argue that it is important to access
  56. an object only by its explicitly declared accessible interface.
  57.     C++ already prevents you accessing explictly declared
  58. and visible *inaccessible* members (private ones, for example).
  59. To raise the accessibility of *invisible* members above that
  60. of visible but inaccessible members seems to be dangerous.
  61. >
  62. >    After all, inheritance allows us to break the "closedness"
  63. >    of a base class' behaviour, by overriding virtual functions.
  64.  
  65.     No it does not. The base is BOTH open and closed, and this
  66. is the open/closed principle. In that C++ does not provide
  67. complete semantic checking (no other language does either),
  68. it is possible but wrong to change the semantics of a class
  69. via a virtual function so the 'isA' relationship of derived to base
  70. is violated. 
  71.  
  72.     The ability of a module to be closed and also open
  73. is what make polymorphism possible, and conversely, breaking
  74. the closedness will destroy the openess, that is, polymorphism.
  75.  
  76.     Polymorphism offers an assurance that we are free
  77. to ARBITRARILY derive new classes from a base, and any or
  78. none of these classes may be substituted for the base.
  79. (References or pointers at least).
  80.  
  81.     As a result it is possible to write functions f(Base&)
  82. that work correctly not only on base objects, but ANY object
  83. derived from base.
  84.  
  85.     If the function f(Base&) calls a function g(Base&)
  86. which silently performs special actions if the reference
  87. is to some PARTICULAR derived class, then changes to
  88. the object can be made NOT conforming to the assumed
  89. invariants of the base, and so f may fail to operate
  90. correctly.
  91.  
  92.     I will try to construct an example.
  93.  
  94.     class X {
  95.     protected: 
  96.         int x;
  97.     public:
  98.         X(int i) : x(i) {}
  99.         int get()const {return x;}
  100.     };
  101.  
  102.     f(X& a){ int n=a.get(); g(a); assert(n==a.get());}
  103.  
  104. I would expect the assertion to obtain because class X provides
  105. no public mechanism to modify 'x', and f has been granted and grants
  106. to g only public access to the X object a.
  107.         
  108.     class Y : public X {
  109.     public:
  110.         Y(int i) : X(i) {}
  111.         void set(int i){x=y;}
  112.     };
  113.  
  114.     Y aY(3);
  115.     f(Y);
  116.  
  117. Now the assertion may fail if 'g' downcasts to Y.
  118. IMHO this is 'bad' because it means in effect
  119. that the protected interface is public. Closure is broken
  120. because the separately compiled routine f now needs to
  121. know of Y if g does. In effect to maintain integrity,
  122. f must ALSO check if the object is of type Y, which
  123. means that it cannot be closed until g is, defeating
  124. the point of separate compilation-- in effect a function
  125. f cannot depend on a function g to conform to its 
  126. declared interface.
  127. >
  128. >    The importance is that in neither case do you have to
  129. >    intervene with the source or compiled code of the base
  130. >    class to do what you want.  This is considered good
  131. >    for behaviour (overriding virtuals), so why not also
  132. >    for interfaces (unrestricted but safe downcasting)?
  133.  
  134.     It is sound for virtuals because they are declared!
  135. They are there PRECISELY to allow this ability. Because the
  136. base is closed and open one can have arbitrary derived classes,
  137. and should be able to DEPEND on this fact. It is USEFUL
  138. to know that arbitrary derivation can occur. The arbitrariness
  139. is a powerful constraint--you may not depend on any particular
  140. derived class behaviour. As a consequence, you MAY depend on
  141. all the objects behaving withing the constraints of the base
  142. class interface.
  143.  
  144.     The ability to downcast implies that the protected
  145. interface of a base class is not protected, it might just
  146. as well have been made public.
  147. >
  148. >    To conclude:
  149. >
  150. >    A base class intended for future inheritance (even by
  151. >    other programmers) should support overriding behaviour
  152. >    using virtual member functions, and should support
  153. >    interface extension by runtime-checked, safe downcasting.
  154. >
  155. >    This lessens the risk that future users without access to the
  156. >    source code will get stuck when deriving from the base class.
  157.  
  158.  
  159.     No, what it does is opens the possibility for programmers
  160. to destroy the functionality of existing working and closed
  161. routines by misunderstanding what polymorphism is and using it
  162. instead for heteromorphism. The point of polymorphism is that
  163. your should not rely on derived class behaviour in routines
  164. given a base object, and you shouldn't need to.
  165.  
  166.     An derived object which 'isA' base  is not merely
  167. 'a bit like a base', but IS precisely a base--no more, no less.
  168.  
  169.     When inheritance is used to add new members only, that
  170. is equivalent to composition.
  171.  
  172.     When the two techniques are combined we have a mix
  173. of polymorphism and heteromorphism. The two should be clearly
  174. separated. Tagged pointers provide one way to do this.
  175. Downcasting blurs the issue: it pretends downcasting
  176. is a mechanism associated with polymorphism, and in so doing
  177. destroys it.
  178.  
  179. -- 
  180. ;----------------------------------------------------------------------
  181.         JOHN (MAX) SKALLER,         maxtal@extro.ucc.su.oz.au
  182.     Maxtal Pty Ltd, 6 MacKay St ASHFIELD, NSW 2131, AUSTRALIA
  183. ;--------------- SCIENTIFIC AND ENGINEERING SOFTWARE ------------------
  184.