home *** CD-ROM | disk | FTP | other *** search
- Newsgroups: comp.lang.c++
- Path: sparky!uunet!munnari.oz.au!metro!extro.ucc.su.OZ.AU!maxtal
- From: maxtal@extro.ucc.su.OZ.AU (John MAX Skaller)
- Subject: Re: Downcasting (was: Re: run-time type checking)
- Message-ID: <1992Aug15.170141.14667@ucc.su.OZ.AU>
- Sender: news@ucc.su.OZ.AU
- Nntp-Posting-Host: extro.ucc.su.oz.au
- Organization: MAXTAL P/L C/- University Computing Centre, Sydney
- References: <9222715.29197@mulga.cs.mu.OZ.AU> <4823@holden.lulea.trab.se>
- Date: Sat, 15 Aug 1992 17:01:41 GMT
- Lines: 171
-
- In article <4823@holden.lulea.trab.se> jbn@lulea.trab.se (Johan Bengtsson) writes:
- >fjh@munta.cs.mu.OZ.AU (Fergus James HENDERSON) writes:
- >: jbn@lulea.trab.se (Johan Bengtsson) writes:
- >:
- >: >maxtal@extro.ucc.su.OZ.AU (John MAX Skaller) writes:
- >: >:
- >: >: class Base { //....
- >: virtual operator Derived1*() { return 0; }
- >: virtual operator Derived2*() { return 0; }
- >: >: };
- >: >:Now what does this do? Well, it preserved th open/closed principle
- >: >:and encapsulation and the explicit interfaces principle.
- >: >
- >: >What if someone using your classes decides to add a Derived3 class
- >: >to which downcasting should be possible? He must modifiy your
- >: >class Base, for which he may not have source code.
- >:
- >: Yes, but that would be a change to the interface!
- >
- > I get your point. I also agree that the above technique
- > is best (most efficient) when you have a limited set
- > of known derived classes.
-
- No one here is arguing about efficiency.
- The set of derived classes is ALWAYS finite in a domestic
- program.
-
- The word 'known' is the keyword.
- >
- >:
- >: The whole point is that the ability to downcast a Base to a derived class
- >: SHOULD be part of the interface. If you want to change the interface, then
- >: of course you are going to need to modify the source code - this does not
- >: violate the open/closed principle.
- >
- > I agree that unrestricted typecasting provides the ability to
- > cleanly extend the interface of a base class, into the interface
- > of any derived class. Although I admit that in a way this does
- > break the "closedness" of the Base class interface, I don't think
- > it does so in a "bad" way.
-
- This is the issue. You might be right, but I'm not convinced.
- In a statically secure system, I argue that it is important to access
- an object only by its explicitly declared accessible interface.
- C++ already prevents you accessing explictly declared
- and visible *inaccessible* members (private ones, for example).
- To raise the accessibility of *invisible* members above that
- of visible but inaccessible members seems to be dangerous.
- >
- > After all, inheritance allows us to break the "closedness"
- > of a base class' behaviour, by overriding virtual functions.
-
- No it does not. The base is BOTH open and closed, and this
- is the open/closed principle. In that C++ does not provide
- complete semantic checking (no other language does either),
- it is possible but wrong to change the semantics of a class
- via a virtual function so the 'isA' relationship of derived to base
- is violated.
-
- The ability of a module to be closed and also open
- is what make polymorphism possible, and conversely, breaking
- the closedness will destroy the openess, that is, polymorphism.
-
- Polymorphism offers an assurance that we are free
- to ARBITRARILY derive new classes from a base, and any or
- none of these classes may be substituted for the base.
- (References or pointers at least).
-
- As a result it is possible to write functions f(Base&)
- that work correctly not only on base objects, but ANY object
- derived from base.
-
- If the function f(Base&) calls a function g(Base&)
- which silently performs special actions if the reference
- is to some PARTICULAR derived class, then changes to
- the object can be made NOT conforming to the assumed
- invariants of the base, and so f may fail to operate
- correctly.
-
- I will try to construct an example.
-
- class X {
- protected:
- int x;
- public:
- X(int i) : x(i) {}
- int get()const {return x;}
- };
-
- f(X& a){ int n=a.get(); g(a); assert(n==a.get());}
-
- I would expect the assertion to obtain because class X provides
- no public mechanism to modify 'x', and f has been granted and grants
- to g only public access to the X object a.
-
- class Y : public X {
- public:
- Y(int i) : X(i) {}
- void set(int i){x=y;}
- };
-
- Y aY(3);
- f(Y);
-
- Now the assertion may fail if 'g' downcasts to Y.
- IMHO this is 'bad' because it means in effect
- that the protected interface is public. Closure is broken
- because the separately compiled routine f now needs to
- know of Y if g does. In effect to maintain integrity,
- f must ALSO check if the object is of type Y, which
- means that it cannot be closed until g is, defeating
- the point of separate compilation-- in effect a function
- f cannot depend on a function g to conform to its
- declared interface.
- >
- > The importance is that in neither case do you have to
- > intervene with the source or compiled code of the base
- > class to do what you want. This is considered good
- > for behaviour (overriding virtuals), so why not also
- > for interfaces (unrestricted but safe downcasting)?
-
- It is sound for virtuals because they are declared!
- They are there PRECISELY to allow this ability. Because the
- base is closed and open one can have arbitrary derived classes,
- and should be able to DEPEND on this fact. It is USEFUL
- to know that arbitrary derivation can occur. The arbitrariness
- is a powerful constraint--you may not depend on any particular
- derived class behaviour. As a consequence, you MAY depend on
- all the objects behaving withing the constraints of the base
- class interface.
-
- The ability to downcast implies that the protected
- interface of a base class is not protected, it might just
- as well have been made public.
- >
- > To conclude:
- >
- > A base class intended for future inheritance (even by
- > other programmers) should support overriding behaviour
- > using virtual member functions, and should support
- > interface extension by runtime-checked, safe downcasting.
- >
- > This lessens the risk that future users without access to the
- > source code will get stuck when deriving from the base class.
-
-
- No, what it does is opens the possibility for programmers
- to destroy the functionality of existing working and closed
- routines by misunderstanding what polymorphism is and using it
- instead for heteromorphism. The point of polymorphism is that
- your should not rely on derived class behaviour in routines
- given a base object, and you shouldn't need to.
-
- An derived object which 'isA' base is not merely
- 'a bit like a base', but IS precisely a base--no more, no less.
-
- When inheritance is used to add new members only, that
- is equivalent to composition.
-
- When the two techniques are combined we have a mix
- of polymorphism and heteromorphism. The two should be clearly
- separated. Tagged pointers provide one way to do this.
- Downcasting blurs the issue: it pretends downcasting
- is a mechanism associated with polymorphism, and in so doing
- destroys it.
-
- --
- ;----------------------------------------------------------------------
- JOHN (MAX) SKALLER, maxtal@extro.ucc.su.oz.au
- Maxtal Pty Ltd, 6 MacKay St ASHFIELD, NSW 2131, AUSTRALIA
- ;--------------- SCIENTIFIC AND ENGINEERING SOFTWARE ------------------
-