From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=0.7 required=5.0 tests=BAYES_00,INVALID_DATE, REPLYTO_WITHOUT_TO_CC autolearn=no autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 103376,c83a22003c320b45 X-Google-Attributes: gid103376,public X-Google-ArrivalTime: 1994-11-04 14:16:42 PST Path: bga.com!news.sprintlink.net!hookup!yeshua.marcam.com!news.mathworks.com!news.duke.edu!news-feed-1.peachnet.edu!gatech!swiss.ans.net!newsgate.watson.ibm.com!watnews.watson.ibm.com!ncohen From: ncohen@watson.ibm.com (Norman H. Cohen) Newsgroups: comp.lang.ada Subject: Re: Initialization Params for Controlled Types Date: 4 Nov 1994 22:16:42 GMT Organization: IBM T.J. Watson Research Center Distribution: world Message-ID: <39ebsa$129i@watnews1.watson.ibm.com> References: <1994Nov4.134412.10010@unix.brighton.ac.uk> Reply-To: ncohen@watson.ibm.com NNTP-Posting-Host: rios8.watson.ibm.com Date: 1994-11-04T22:16:42+00:00 List-Id: In article <1994Nov4.134412.10010@unix.brighton.ac.uk>, je@unix.brighton.ac.uk (John English) writes: |> There is one subtle danger with using procedures as "constructors". |> Imagine the following: |> type BankAccount is tagged private; |> procedure Open (Account : in out BankAccount; |> Name : in String); |> |> Here Open opens a bank account in a particular name. Now if you |> derive a new type CurrentAccount from BankAccount to produce a |> BankAccount that allows overdrafts, you might have this: |> type CurrentAccount is new BankAccount with private; |> procedure Open (Account : in out CurrentAccount; |> Name : in String; |> Overdraft : in Money); |> |> Here's where the danger is: the first procedure (Open for BankAccount) |> is a primitive of BankAccount and will be inherited by CurrentAccount. |> It's therefore possible to initialise a CurrentAccount as if it were |> a BankAccount, which might leave the overdraft limit uninitialised: |> C : CurrentAccount; |> ... |> Open (C, "Fred Bloggs"); -- oops! |> |> Okay, you can provide a default overdraft limit of 0.00, so this is |> not an entirely convincing example; the point is that there is a |> danger here which is non-obvious, since you might forget that there |> is a second Open for CurrentAccounts which is provided due to inheritance. There is a good reason the example is not convincing: It violates the principle that the inheritance hierarchy should reflect an IS-A relationship. CurrentAccount should be derived from BankAccount only if a "current account" is a special kind of "bank account". If it is, then it should have all the properties of bank accounts--including the ability to open it by specifying just a name. After all, when you add CurrentAccount to the class rooted at BankAccount, you are asserting that all the primitive operations of BankAccount can be applied to BankAccount'Class objects with any tag. You can look at this as a problem with the operation Open or a problem with the hierarchy. If you view it as a problem with the hierarchy, a solution is to introduce a new abstract class, say Universal_Account, that declares the operations that truly are meaningful for ALL bank accounts, and derive BankAccount and CurrentAccount from Universal_Account. Open is not one of these operations. Two distinct versions of Open would be declared for the two derived types. |> C++ sidesteps this problem by making constructors non-inheritable. |> The only way to accomplish this in 9X is to make sure that Open isn't |> a primitive (e.g. by shoving it in a child package) which is fairly |> painful. Note that the danger of an inadvertent error could be avoided in Ada 9X by making the constructor a function returning a BankAccount value: function New_Bank_Account (Name: String) return BankAccount; Then the version inherited by CurrentAccount would be abstract. Calls on the inherited version would be illegal, thus alerting the programmer to the fact that something is amiss. -- Norman H. Cohen ncohen@watson.ibm.com