+ All Categories

cppcv3

Date post: 30-May-2018
Category:
Upload: aeroarunn
View: 225 times
Download: 0 times
Share this document with a friend
63
C++?? A Critique of C++ and Programming and Language Trends of the 1990s 3rd Edition Ian Joyner The views in this critique in no way reflect the position of my employer © Ian Joyner 1996
Transcript

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 1/63

C++??A Critique of C++

and Programming and Language Trends of the 1990s

3rd Edition

Ian Joyner

The views in this critique in no way reflect the position of my employer

© Ian Joyner 1996

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 2/63

C++?? ii

3rd Edition © Ian Joyner 1996

1. INTRODUCTION .................................................................................................................................................1

2. THE ROLE OF A PROGRAMMING LANGUAGE...........................................................................................2

2.1 PROGRAMMING ..................................................................................................................................................32.2 COMMUNICATION , ABSTRACTION AND PRECISION .................................................................................................42.3 NOTATION .........................................................................................................................................................52.4 TOOL INTEGRATION ............................................................................................................................................5

2.5 CORRECTNESS ....................................................................................................................................................52.6 TYPES ................................................................................................................................................................72.7 REDUNDANCY AND CHECKING ............................................................................................................................72.8 ENCAPSULATION ................................................................................................................................................82.9 SAFETY AND COURTESY CONCERNS ....................................................................................................................82.10 IMPLEMENTATION AND DEPLOYMENT CONCERNS ...............................................................................................92.11 C ONCLUDING REMARKS ....................................................................................................................................9

3. C++ SPECIFIC CRITICISMS ............. ............. ............... ............. ............... ............. ............. ............... ............. ...9

3.1 V IRTUAL FUNCTIONS ..........................................................................................................................................93.2 GLOBAL ANALYSIS ...........................................................................................................................................123.3 TYPE-SAFE LINKAGE .........................................................................................................................................133.4 FUNCTION OVERLOADING .................................................................................................................................143.5 THE NATURE OF INHERITANCE ..........................................................................................................................15

3.6 MULTIPLE INHERITANCE ...................................................................................................................................163.7 V IRTUAL CLASSES ............................................................................................................................................173.8 TEMPLATES ......................................................................................................................................................173.9 NAME OVERLOADING .......................................................................................................................................193.10 NESTED CLASSES ............................................................................................................................................213.11 G LOBAL ENVIRONMENTS ................................................................................................................................223.12 POLYMORPHISM AND INHERITANCE .................................................................................................................233.13 TYPE CASTS ...................................................................................................................................................233.14 RTTI AND TYPE CASTS ...................................................................................................................................243.15 NEW TYPE CASTS ...........................................................................................................................................253.16 JAVA AND CASTS ............................................................................................................................................263.17 ‘.’ AND ‘->’ ...................................................................................................................................................263.18 ANONYMOUS PARAMETERS IN CLASS DEFINITIONS ...........................................................................................273.19 NAMELESS CONSTRUCTORS .............................................................................................................................273.20 CONSTRUCTORS AND TEMPORARIES ................................................................................................................273.21 OPTIONAL PARAMETERS .................................................................................................................................283.22 BAD DELETIONS .............................................................................................................................................283.23 LOCAL ENTITY DECLARATIONS ........................................................................................................................283.24 MEMBERS ......................................................................................................................................................293.25 INLINES ..........................................................................................................................................................293.26 FRIENDS .........................................................................................................................................................303.27 CONTROLLED EXPORTS VS FRIENDS .................................................................................................................303.28 STATIC ...........................................................................................................................................................313.29 UNION ............................................................................................................................................................323.30 STRUCTS ........................................................................................................................................................323.31 TYPEDEFS ......................................................................................................................................................323.32 NAMESPACES ..................................................................................................................................................323.33 HEADER FILES ................................................................................................................................................333.34 CLASS INTERFACES .........................................................................................................................................343.35 CLASS HEADER DECLARATIONS .......................................................................................................................343.36 GARBAGE COLLECTION ...................................................................................................................................343.37 LOW LEVEL CODING ........................................................................................................................................353.38 S IGNATURE VARIANCE ....................................................................................................................................353.39 PURE VIRTUAL FUNCTIONS .............................................................................................................................363.40 PROGRAMMING BY CONTRACT ........................................................................................................................363.41 C++ AND THE SOFTWARE LIFECYCLE ...............................................................................................................373.42 CASE T OOLS .................................................................................................................................................383.43 REUSABILITY AND COMMUNICATION ...............................................................................................................393.44 REUSABILITY AND TRUST ................................................................................................................................393.45 REUSABILITY AND COMPATIBILITY ..................................................................................................................40

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 3/63

C++?? iii

3rd Edition © Ian Joyner 1996

3.46 REUSABILITY AND PORTABILITY ......................................................................................................................403.47 IDIOMATIC PROGRAMMING ..............................................................................................................................413.48 CONCURRENT PROGRAMMING .........................................................................................................................413.49 STANDARDISATION , STABILITY AND MATURITY ..............................................................................................423.50 COMPLEXITY ..................................................................................................................................................433.51 C++: THE OVERWHELMING OOL OF CHOICE? .................................................................................................44

4. GENERIC C CRITICISMS ................................................................................................................................45

4.1 POINTERS .........................................................................................................................................................454.2 ARRAYS ...........................................................................................................................................................464.3 FUNCTION ARGUMENTS ....................................................................................................................................474.4 VOID AND VOID * ..............................................................................................................................................484.5 VOID FN () ........................................................................................................................................................484.6 FN ().................................................................................................................................................................494.7 FN (VOID) .........................................................................................................................................................504.8 METADATA IN STRINGS .....................................................................................................................................504.9 ++, --...............................................................................................................................................................504.10 DEFINES .........................................................................................................................................................514.11 NULL VS 0 ....................................................................................................................................................514.12 CASE SENSITIVITY ..........................................................................................................................................524.13 ASSIGNMENT OPERATOR .................................................................................................................................534.14 CHAR ; SIGNED AND UNSIGNED .........................................................................................................................534.15 SEMICOLONS ..................................................................................................................................................534.16 BOOLEANS .....................................................................................................................................................544.17 COMMENTS ....................................................................................................................................................544.18 CPAGHE ++I ....................................................................................................................................................54

4.18.1 Cpaghe++i Gotos ..................................................................................................................................544.18.2 Cpaghe++i Globals ...............................................................................................................................554.18.3 Cpaghe++i Pointers...............................................................................................................................55

5. CONCLUSIONS..................................................................................................................................................56

6. BIBLIOGRAPHY ...............................................................................................................................................58

7. WEBLIOGRAPHY .............. ............... .................. ............... .................. ............... .................. .................. .......... 59

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 4/63

C++?? 1

3rd Edition © Ian Joyner 1996

1. IntroductionThis is now the third edition of this critique; it hasbeen four years since the last edition. The mainfactor to precipitate a new edition is that there arenow more environments and languages availablethat rectify the problems of C++. The last editionwas addressed to people who were consideringadopting C++, in particular managers who would

have to fund projects. There are now more choices,so comparison to the alternatives makes the critiqueless hypothetical. The critique was not meant as anacademic treatise, although some of the aspectsrelating to inheritance, etc., required a bit of technical knowledge.

The critique is long; it would be good if it wereshorter, but that would be possible only if there wereless flaws in C++. Even so, the critique is notexhaustive of the flaws: I find new traps all the time.Instead of documenting every trap, the critiqueattempts to arrange the traps into categories andprinciples. This is because the traps are not just oneoff things, but more deeply rooted in the principles

of C++. Neither is the critique a repository of ‘guesswhat this obscure code does’ examples.One desired outcome of this critique is that it

should awaken the industry about the C++ myth andthe fact that there are now viable alternatives to C++that do not suffer from as many technical problems.The industry needs less hype and more sensibleprogramming practices. No language can be perfectin every situation, and tradeoffs are sometimesnecessary, but you can now feel freer to choose alanguage which is more closely suited to your needs.The alternatives to C++ provide no silver bullet , butsignificantly reduce the risks and costs of softwaredevelopment compared to C++. The alternatives do

not suffer under the complexities of C++ and do notburden the programmer with many trivialities whichthe compiler should handle; and they avoid many of the flaws and inanities of C/C++.

The language events which have made an updatedesirable are the introduction of Java, the wideravailability of more stable versions of Eiffel, and thefinalisation of the Ada 95 standard. Java inparticular set out to correct the flaws of C++, andmost sections in the original critique now makesome comment on how Java addresses the problems.Eiffel never did have the same flaws as C++, andhas been around since long before the originalcritique. Eiffel was designed to be object-oriented

from the ground up, rather than a bolt-on . Javaoffers better integration with OO than C++. Nowthat there are language comparisons in the critiquethe arguments are less hypothetical, and thecriticisms of C++ are more concrete.

Another factor has been the publishing of BjarneStroustrup’s “Design and Evolution of C++”[Stroustrup 94]. This has many explanations of theproblems of extending C with object-orientedextensions while retaining compatibility with C. Inmany ways, Stroustrup reinforces comments that Imade in the original critique, but I differ from

Stroustrup in that I do not view the flaws of C++ asacceptable, even if they are widely known, andmany programmers know how to avoid the traps.Programming is a complex endeavour: complex andflawed languages do not help.

A question which has been on my mind in thelast few years is when is OO applicable? OO is auniversal paradigm. It is very general and powerful.

There is nothing that you could not program in it.But is this always appropriate? Lower levelprogrammers have tended to keep writing suchthings as device drivers in C. It is not lower levelsthat I am interested in, but the higher levels. OOmight still be too low level for a number of applications. A recent book [Shaw 96] suggests thatsoftware engineers are too busy designing systemsin terms of stacks, lists, queues, etc., instead of adopting higher level, domain-orientedarchitectures. [Shaw 96] offers some hope to theindustry that we are learning how to architect tosolve problems, rather than distorting problems to fitparticular technologies and solutions.

For instance, commercial and businessprogramming might be faster using a paradigminvolving business objects. While these could beprovided in an OO framework, the generality is notneeded in commercial processing, and will slow andlimit the flexibility of the development process. Byanalogy, walking is a fine mode of transport, but doI choose to walk everywhere? There seems to be apotentially large market for specialised paradigms,which support rapid application development (RAD)techniques. These paradigms may be based on someOO language, framework and libraries in thebackground. In anything though, we should becautious, as this is an industry particularly prone tobuzzwords and fads.

The second edition generated a lot of interest,and it was published in a number of places:Software Design in Japan translated it into Japanese,and published it over a series of months in 1993; itwas published in an abridged form in TOOLSPacific 1992; it was also published in Gregory’s ASeries Technical Journal. However, I resistedhanding over copyright to anyone, as I wanted thepaper to be freely available on the Internet; it is nowavailable on more sites than I know about. Mythanks to all those who have been so supportive of the 2nd edition.

Another reason for the 3rd edition is that the

original critique was very much a product of newsgroup discussions. In this edition, I haveattempted to at least improve the readability andflow, while not changing the overall structure orembarking on a complete rewrite. The primary goalhas been to annotate the original with comparisonsto Java and Eiffel.

C++ has become even more widely used overthe last few years. However, people are starting torealise that it is not the answer to all programmingproblems, or that retaining compatibility with C is agood thing. In some sectors there has been a

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 5/63

C++?? 2

3rd Edition © Ian Joyner 1996

backlash, precipitated by the fact that people havefound the production of defect free quality softwarean extremely difficult and costly task. OO has beenover-hyped, but neither are its real benefits presentin C++.

It is important and timely to question C++’s suc-cess. Several books are already published on thesubject [Sakkinen 92], [Yoshida 92], and [Wiener

95]. A paper on the recommended practices for usein C++ [Ellemtel 92] suggests “C++ is a difficultlanguage in which there may be a very fine linebetween a feature and a bug. This places a largeresponsibility upon the programmer.” Is this aresponsibility or a burden? The ‘fine line’ is a resultof an unnecessarily complicated language definition.The C++ standardisation committee warns “C++ isalready too large and complicated for our taste”[X3J16 92].

Sun’s Java White Paper [Sun 95] says that indesigning Java, “The first step was to eliminateredundancy from C and C++. In many ways, the Clanguage evolved into a collection of overlapping

features, providing too many ways to do the samething, while in many cases not providing neededfeatures. C++, even in an attempt to add “classes inC” merely added more redundancy while retainingthe inherent problems of C.”

The designer of Eiffel, Bertrand Meyer, states inthe appendix “On language design and evolution” in[Meyer 92] some guiding principles of languagedesign: simplicity vs complexity, uniqueness,consistency. “The Principle of Uniqueness,” Meyersays, “is easily expressed: the language shouldprovide one good way to express every operation of interest; it should avoid providing two.”

Meyer has produced a seminal work on OO:

Object-oriented Software Construction , [Meyer 88].All software engineers and object-orientedpractitioners should read and absorb this work. Acompletely revised 2nd edition is soon to appear. Alater short book “Object Success” is directed tomanagers (probably the reason for the pun in thename), with an overview of OO, [Meyer 95].

While C programmers can immediately use C++to write and compile C programs, this does not takeadvantage of OO. Many see this as a strength, but itis often stated that the C base is C++’s greatestweakness. However, C++ adds its own layers of complexity, like its handling of multiple inheritance,overloading, and others. I am not so sure that C is

C++’s greatest weakness. Java has shown that inremoving C constructs that do not fit with object-oriented concepts, that C can provide an acceptable,albeit not perfect base.

Adoption of C++ does not suddenly transform Cprogrammers into object-oriented programmers. Acomplete change of thinking is required, and C++actually makes this difficult. A critique of C++cannot be separated from criticism of the C baselanguage, as it is essential for the C++ programmerto be fluent in C. Many of C’s problems affect theway that object-orientation is implemented and used

in C++. This critique is not exhaustive of theweaknesses of C++, but it illustrates the practicalconsequences of these weaknesses with respect tothe timely and economic production of qualitysoftware.

This paper is structured as follows: section 2considers the role of a programming language;section 3 examines some specific aspects of C++;

section 4 looks specifically at C; and the conclusionexamines where C++ has left us, and considers thefuture.

I have tried to keep the sections reasonably self contained, so that you can read the sections thatinterest you, and use the critique in a reference style.There are some threads that occur throughout thecritique, and you will find some repetition of ideasto achieve self contained sections.

Having said that, I hope that you find thiscritique useful, and enjoyable: so please feel free todistribute it to your management, peers and friends.

2. The Role of a ProgrammingLanguageA programming language functions at manydifferent levels and has many roles, and should beevaluated with respect to those levels and roles.Historically, programming languages have had alimited role, that of writing executable programs. Asprograms have grown in complexity, this role alonehas proved insufficient. Many design and analysistechniques have arisen to support other necessaryroles.

Object-oriented techniques help in the analysisand design phases; object-oriented languages tosupport the implementation phase of OO, but inmany cases these lack uniformity of concepts,integration with the development environment andcommonality of purpose. Traditional problematicsoftware practices are infiltrating the object-orientedworld with little thought. Often these techniquesappeal to management because they are outwardlyorganised: people are assigned organisational rolessuch as project manager, team leader, analyst,designer and programmer. But these techniques aresimplistic and insufficient, and result in demotivatedand uncreative environments.

Object-orientation, however, offers a betterrational approach to software development. Thecomplementary roles of analysis, design,implementation and project organisation should bebetter integrated in the object-oriented scheme. Thisresults in economical software production, and morecreative and motivated environments.

The organisation of projects also required toolsexternal to the language and compiler, like ‘make.’A re-evaluation of these tools shows that often thedivision of labour between them has not been donealong optimal lines: firstly, programmers need to doextra bookkeeping work which could be automated;and secondly, inadequate separation of concerns hasresulted in inflexible software systems.

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 6/63

C++?? 3

3rd Edition © Ian Joyner 1996

C++ is an interesting experiment in adapting theadvantages of object-orientation to a traditionalprogramming language and developmentenvironment. Bjarne Stroustrup should berecognised for having the insight to put the twotechnologies together; he ventured into OO not onlybefore solutions were known to many issues, butbefore the issues were even widely recognised. He

deserves better than a back full of arrows. But inretrospect, we now treat concepts such as multipleinheritance with a good deal of respect, and realisethat the Unix development environment with limitedlinker support does not provide enough compilersupport for many of the features that should be in ahigh level language.

There are solutions to the problems that C++uncovered. C++ has gone down a path in research,but now we know what the problems are and how tosolve them. Let’s adopt or develop such languages.Fortunately, such languages have been developed,which are of industrial strength, meant forcommercial projects, and are not just academic

research projects. It is now up to the industry toadopt them on a wider scale.C++, however, retains the problems of the old

order of software production. C++ has an advantageover C as it supports many facets of object-orientation. These can be used for some analysis anddesign. The processes of analysis, design, andorganisation, however, are still largely external toC++. C++ has not realised the important advantagesof integrated software development that leads toimproved economies of software production.

Java is an interesting development taking adifferent approach to C++: strict compatibility withC is not seen as a relevant goal. Java is not the only

C based alternative to C++ in the object-orientedworld. There has also been Objective-C from BradCox, and mainly used in NeXT’s OpenStepenvironment. Objective-C is more like Smalltalk, inthat all binding is done dynamically at run time.

A language should not only be evaluated from atechnical point of view, considering its syntactic andsemantic features; it should also be analysed fromthe viewpoint of its contribution to the entiresoftware development process. A language shouldenable communication between project membersacting at different levels, from management, who setenterprise level policies, to testers, who must test theresult. All these people are involved in the general

activity of programming, so a language shouldenable communication between project membersseparated in space and time. A single programmer isnot often responsible for a task over its entirelifetime.

2.1 ProgrammingProgramming and specification are now seen as thesame task. One man’s specification is another’sprogram. Eventually you get to the point of processing a specification with a compiler, whichgenerates a program which actually runs on a

computer. Carroll Morgan banishes the distinctionbetween specifications and programs: “To us theyare all programs.” [Morgan 90]. Programming is aterm that not only refers to implementation;programming refers to the whole process of analysis, design and implementation.

The Eiffel language integrates the concept of specification and programming, rejecting the

divided models of the past in favour of a newintegrated approach to projects. Eiffel achieves thisin several ways: it has a clean clear syntax which iseasy to read, even by non-programmers; it hastechniques such as preconditions and postconditionsso that the semantics of a routine can be clearlydocumented, these being borrowed from formalspecification techniques, but made easy for the ‘restof us’ to use; and it has tools to extract the abstractspecification from the implementation details of aprogram. Thus Eiffel is more than just a language,providing a whole integrated developmentenvironment.

Chris Reade [Reade 89] gives the following

explanation of programming and languages. “One,rather narrow, view is that a program is a sequenceof instructions for a machine. We hope to show thatthere is much to be gained from taking the muchbroader view that programs are descriptions of values, properties, methods, problems and solutions.The role of the machine is to speed up themanipulation of these descriptions to provide so-lutions to particular problems. A programminglanguage is a convention for writing descriptionswhich can be evaluated.”

[Reade 89] also describes programming as beinga “Separation of concerns”. He says:

“The programmer is having to do several things

at the same time, namely,(1) describe what is to be computed;(2) organise the computation sequencing into

small steps;(3) organise memory management during the

computation.”Reade continues, “Ideally, the programmer shouldbe able to concentrate on the first of the three tasks(describing what is to be computed) without beingdistracted by the other two, more administrative,tasks. Clearly, administration is important but byseparating it from the main task we are likely to getmore reliable results and we can ease theprogramming problem by automating much of theadministration.

“The separation of concerns has otheradvantages as well. For example, program provingbecomes much more feasible when details of sequencing and memory management are absentfrom the program. Furthermore, descriptions of whatis to be computed should be free of such detailedstep-by-step descriptions of how to do it if they areto be evaluated with different machine architectures.Sequences of small changes to a data object held ina store may be an inappropriate description of how

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 7/63

C++?? 4

3rd Edition © Ian Joyner 1996

to compute something when a highly parallelmachine is being used with thousands of processorsdistributed throughout the machine and local ratherthan global storage facilities.

“Automating the administrative aspects meansthat the language implementor has to deal withthem, but he/she has far more opportunity to makeuse of very different computation mechanisms with

different machine architectures.”These quotes from Reade are a good summaryof the principles from which I criticise C++. WhatReade calls administrative tasks, I call bookkeeping .Bookkeeping adds to the cost of softwareproduction, and reduces flexibility which in turnadds more to the cost. C and C++ are often criticisedfor being cryptic. The reason is that C concentrateson points 2 and 3, while the description of what is tobe computed is obscured.

High level languages describe ‘what’ is to becomputed; that is the problem domain. ‘How’ acomputation is achieved is in the low-level machine-oriented deployment domain. Automating the

bookkeeping tasks enhances correctness,compatibility, portability and efficiency.Bookkeeping tasks arise from having to specify‘how’ a computation is done. Specifying ‘how’things are done in one environment hindersportability to other platforms.

The most significant way high level languagesreplace bookkeeping is using a declarative approach,whereas low level languages use operators, whichmake them more like assemblers. C and C++provide operators rather than the declarativeapproach, so are low level. The declarative approachcentralises decisions and lets the compiler generatethe underlying machine operators. With the operator

approach, the bookkeeping is on the programmer touse the correct operator to access an entity, and if adecision changes, the programmer will have tochange all operators, rather than change the singledeclaration and simply recompiling. Thus in C andC++ the programmer is often concerned with theaccess mechanisms to data, whereas high levellanguages hide the implementation detail, makingprogram development and maintenance far moreflexible.

While C and C++ syntax is similar to high levellanguage syntax, C and C++ cannot be consideredhigh level, as they do not remove bookkeeping fromthe programmer that high level languages should,

requiring the compiler to take care of these details.The low level nature of C and C++ severely impactsthe development process.

The most important quality of a high levellanguage is to remove bookkeeping burden from theprogrammer in order to enhance speed of development, maintainability and flexibility. Thisattribute is more important than object-orientationitself, and should be intrinsic to any modernprogramming paradigm. C++ more than cancels thebenefits of OO by requiring programmers to perform

much of the bookkeeping instead of it beingautomated.

The industry should be moving towards theseideals, which will help in the economic productionof software, rather than the costly techniques of today. We should consider what we need, and assessthe problems of what we have against that. Object-orientation provides one solution to these problems.

The effectiveness of OO, however, depends on thequality of its implementation.

2.2 Communication, abstraction andprecisionThe primary purpose of any language iscommunication. A specification is communicationfrom one person to another entity of a task to befulfilled. At the lowest level, the task to be fulfilledis the execution of a program by a computer. At thenext level it is the compilation of a program by acompiler. At higher levels, specificationscommunicate to other people what is to beaccomplished by the programming task. At thelowest level, instructions must be preciselyexecuted, but there is no understanding; it is purelymechanical. At higher levels, understanding isimportant, as human intelligence is involved, whichis why enlightened management practices emphasisetraining rather than forced processes. This is not tosay that precision is not important; precision at thehigher levels is of utmost importance, or the rest of the endeavour will fail. Most projects fail due tolack of precision in the requirements and other earlystages.

Unfortunately, often those who are least skilledin programming work at the higher levels, sospecifications lack the desirable properties of

abstraction and precision. Just as in the Dilbert Principle [Adams 96], the least effectiveprogrammers are promoted to where they willseemingly do the least damage. This is not quite thewinning strategy that it seems, as that is where theyactually do the most damage, as teams of confusedprogrammers are then left to straighten out theirspecifications, while the so called analysts moveonto the next project or company to sew the seeds of disaster there.

(Indeed, since many managers have not read orunderstood the works of Deming [Deming 82],[L&S 95], De Marco and Lister [DM&L 87], andTom Peters’ later works, the message that the

physical environment and attitudes of the work place leads to quality has not got through. Perhapsthe humour of Scott Adams is now the only way thismessage will have impact.)

At higher levels, abstraction facilitatesunderstanding. Abstraction and precision are bothimportant qualities of high level specifications.Abstraction does not mean vagueness, nor theabandonment of precision. Abstraction means theremoval of irrelevant detail from a certainviewpoint. With an abstract specification, you are

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 8/63

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 9/63

C++?? 6

3rd Edition © Ian Joyner 1996

In the first figure the black box represents the realinconsistencies, which must be covered by eithercompile-time checks or run-time checks.

In the scenario of this diagram, checks areinsufficient so obscure failures occur at run-time,varying from obscure run-time crashes to strangelywrong results to being lucky and getting away withit. Currently too much software development isbased on programming until you are in the luckystate, known as hacking . This sorry situation in theindustry must change by the adoption of betterlanguages to remove the ad hoc nature of development.

Some feel that compiler checks are restrictiveand that run-time checks are not efficient, sopassionately defend this model, as programmers aresupposedly trustworthy enough to remove the rest of the real consistencies. Although most programmersare conscientious and trustworthy people, this leavestoo much to chance. You can produce defect-freesoftware this way, as long as the programmer doesnot introduce the inconsistencies in the first place,but this becomes much more difficult as the size andcomplexity of a software system increases, andmany programmers become involved. The realinconsistencies are often removed by hacking untilthe program works, with a resultant dependency ontesting to find the errors in the first place.Sometimes companies depend on the customers toactually do the testing and provide feedback aboutthe problems. While fault reporting is an essentialpath of communication from the customer, it mustbe regarded as the last and most costly line of defence.

C and C++ are in this category. Softwareproduced in these languages is prone to obscurefailures.

The second figure, shows that the language detectsinconsistencies beyond the real inconsistency box.These are false alarms. The run-time environmentalso doubles up on inconsistencies that the compiler

has detected and removed, which results in run-timeinefficiency. The language will be seen asrestrictive, and the run-time as inefficient. Youwon’t get any obscure crashes, but the language willget in the way of some useful computations. Pascalis often (somewhat unfairly) criticised for being toorestrictive.

The above figure shows an even worse situation,where the compiler generates false alarms onfictional inconsistencies, does superfluous checks atrun-time, but fails to detect real inconsistencies.

The best situation would be for a compiler tostatically detect all inconsistencies without falsealarms. However, it is not possible to staticallydetect all errors with the current state of technology,as a significant class of inconsistencies can only bedetected at run-time; inconsistencies such as: divideby zero; array index out of bounds; and a class of type checks that are discussed in the section onRTTI and type casts.

The current ideal is to have the detectable andreal inconsistency domains exactly coincide, with asfew checks left to run-time as possible. This has twoadvantages: firstly, that your run-time environmentwill be a lot more likely to work without exceptions,so your software is safer; and secondly, that yoursoftware is more efficient, as you don’t need somany run-time checks. A good language willcorrectly classify inconsistencies that can bedetected at compile time, and those that must be leftuntil run-time.

This analysis shows that as some inconsistenciescan only be detected at run-time, and that suchdetection results in exceptions that exceptionhandling is an exceedingly important part of software. Unfortunately, exception handling has notreceived serious enough attention in mostprogramming languages.

Eiffel has been chosen for comparison in thiscritique as the language that is as close to the idealas possible; that is, all inconsistencies are covered,while false alarms are minimised, and the detectable

Compile Time

Run Time

CompileTime

RunTime Compile

Time

Run

CompileTime

Run Time

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 10/63

C++?? 7

3rd Edition © Ian Joyner 1996

inconsistencies are correctly categorised as compile-time or run-time. Eiffel also pays serious attentionto exception handling.

2.6 TypesIn order to produce correct programs, syntax checksfor conformance to a language grammar are notsufficient: we should also check semantics. Somesemantics can be built into the language, but mostlythis must be specified by the programmer about thesystem being developed.

Semantics checking is done by ensuring that aspecification conforms to some schema. Forexample, the sentence: “The boy drank the computerand switched on the glass of water” is grammaticallycorrect, but nonsense: it does not conform to themental schema we have of computers and glasses of water. A programming language should includetechniques for the detection of similar nonsense. Thetechnique that enables detection of the abovenonsense is types. We know from the computer’stype that it does not have the property ‘drinkable’.Types define an entity’s properties and behaviour.Programming languages can either be typed oruntyped; typed languages can be statically typed ordynamically typed. Static typing ensures at compiletime that only valid operations are applied to anentity. In dynamically typed languages, typeinconsistencies are not detected until run-time.Smalltalk is a dynamically typed language, not anuntyped language. Eiffel is statically typed.

C++ is statically typed, but there are manymechanisms that allow the programmer to render iteffectively untyped, which means errors are notdetected until a serious failure. Some argue thatsometimes you might want to force someone todrink a computer, so without these facilities, thelanguage is not flexible enough. The correct solutionthough is to modify the design, so that now thecomputer has the property drinkable. Underminingthe type system is not needed, as the type system iswhere the flexibility should be, not in the ability toundermine the type system. Providing andmodifying declarations is declarative programming.Eiffel tends to be declarative with a simpleoperational syntax, whereas C++ provides a plethoraof operators.

Defining complex types is a central concept of object-oriented programming: “Perhaps the mostimportant development [in programming languages]has been the introduction of features that supportabstract data types (ADTs). These features allowprogrammers to add new types to languages that canbe treated as though they were primitive types of thelanguage. The programmer can define a type and acollection of constants, functions, and procedures onthe type, while prohibiting any program using thistype from gaining access to the implementation of the type. In particular, access to values of the type isavailable only through the provided constants,functions, and procedures.” [Bruce 96].

Object-oriented programming also provides twospecific ways to assemble new and complex types:“objects can be combined with other types inexpressive and efficient ways (composition andhierarchy) to define new, more complex types.”[Ege 96].

2.7 Redundancy and Checking

Redundant information is often needed to enablecorrectness checking. Type definitions define theelements in a system’s universe, and the propertiesgoverning the valid combinations and interactions of the elements. Declarations define the entities in asystem’s universe. The compiler uses redundantinformation for consistency checking, and strips itaway to produce efficient executable systems. Typesare redundant information. You can program in anentirely typeless language: however, this would beto deny the progress that has been made in makingprogramming a disciplined craft, that producescorrect programs economically.

It is a misconception that consistency checks are‘training wheels’ for student programmers, and that‘syntax’ errors are a hindrance to professionalprogrammers. Languages that exploit techniques of schema checking are often criticised as beingrestrictive and therefore unusable for real worldsoftware. This is nonsense and misunderstands thepower of these languages. It is an immatureconception; the best programmers realise thatprogramming is difficult. As a whole, the computingprofession is still learning to program.

While C++ is a step in this direction, it ishindered by its C base, importing such mechanismsas pointers with which you can undermine the logicof the type system. Java has abandoned these C

mechanisms where they hinder: “The Java compileremploys stringent compile-time checking so thatsyntax-related errors can be detected early, before aprogram is deployed in service” [Sun 95]. Theprogramming community has matured in the lastfew years, and while there was vehement argumentagainst such checking in the past by those who sawit as restrictive and disciplinarian, the majority of the industry now accepts, and even demands it.

Checking has also been criticised from anotherpoint of view. This point of view says that checkingcannot guarantee software quality, so why bother?The premise is correct, but the conclusion is wrong.Checking is neither necessary, nor sufficient to

produce quality software. However, it is helpful anduseful, and is a piece in a complicated jig-saw whichshould not be ignored.

In fact there are few things that are necessary forquality software production. Mainly, softwarequality is dependent on the skill and dedication of the people involved, not methodologies ortechniques. There is nothing that is sufficient. AsFred Brooks has pointed out, there is no Silver

Bullet [Brooks 95]. Good craftsmen choose the righttools and techniques, but the result is dependent onthe skill used in applying the tools. Any tool is

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 11/63

C++?? 8

3rd Edition © Ian Joyner 1996

worthless in itself. But the Silver Bullet rationale isnot a valid rationale against adopting betterprogramming languages, tools and environments;unfortunately, Brooks’ article has been misused.

Another example of consistency checking comesfrom the user interface world. Instead of correcting auser after an erroneous action, a good user interfacewill not offer the action as a possibility in the first

place. It is cheaper to avoid error than to fix it. Mostpeople drive their cars with this principle in mind:smash repair is time consuming and expensive.

Program development is a dynamic process;program descriptions are constantly modified duringdevelopment. Modifications often lead toinconsistencies and error. Consistency checks helpprevent such ‘bugs’, which can ‘creep’ into apreviously working system. These checks helpverify that as a program is modified, previousdecisions and work are not invalidated.

It is interesting to consider how much checkingcould be integrated in an editor. The focus of manycurrent generation editors is text. What happens if

we change this focus from text to programcomponents? Such editors might check not onlysyntax, but semantics. Signalling potential errorsearlier and interactively will shorten developmenttimes, alerting programmers to problems, rather thanwasting hours on changes which later have to beundone. Future languages should be defined verycleanly in order to enable such editor technology.

2.8 EncapsulationThere is much confusion about encapsulation,mostly arising from C++ equating encapsulationwith data hiding . The Macquarie dictionary definesthe verb to encapsulate as “to enclose in or as in acapsule. ” The object-oriented meaning of encapsulation is to enclose related data, routines anddefinitions in a class capsule. This does notnecessarily mean hiding.

Implementation hiding is an orthogonal conceptwhich is possible because of encapsulation. Bothdata and routines in a class are classified accordingto their role in the class as interface orimplementation.

To put this another way: first you encapsulateinformation and operations together in a class, thenyou decide what is visible, and what is hiddenbecause it is implementation detail. Most often onlythe interface routines and data should appear atdesign time, the implementation details appearinglater.

Encapsulation provides the means to separatethe abstract interface of a class from itsimplementation: the interface is the visible surfaceof the capsule; the implementation is hidden in thecapsule. The interface describes the essentialcharacteristics of objects of the class which arevisible to the exterior world. Like routines, data in aclass can also be divided into characteristic interfacedata which should be visible, and implementation

data which should be hidden. Interface data are anycharacteristics which might be of interest to theoutside world. For example when buying a car, thepurchaser might want to know data such as theengine capacity and horse-power, etc. However, thefact that it took John Engineer six days to design theengine block is of no interest.

Implementation hiding means that data can only

be manipulated, that is updated, within the class, butit does not mean hiding interface data. If the datawere hidden, you could never read it, in which case,classes would perform no useful function as youcould only put data into them, but never getinformation out.

In order to provide implementation hiding inC++ you should access your data through Cfunctions. This is known as data hiding in C++. It isnot the data that is actually being hidden, but theaccess mechanism to the data. The accessmechanism is the implementation detail that you arehiding. C++ has visible differences between theaccess mechanisms of constants, variables and

functions. There is even a typographic convention of upper case constant names, which makes thedifferences between constants and variables visible.The fact that an item is implemented as a constantshould also be hidden. Most non-C languagesprovide uniform functional access to constants,variables and value returning routines. In the case of variables, functional access means they can be readfrom the outside, but not updated. An importantprinciple is that updates are centralised within theclass.

Above I indicated that encapsulation wasgrouping operations and information together.Where do functions fit into this? The wrong answer

is that functions are operations. Functions areactually part of the information, as a function returnsinformation derived from an object’s data to theoutside world.

This theme and its adverse consequences, thatplace the burden of encapsulation on theprogrammer rather than being transparent, recurthroughout this critique.

2.9 Safety and Courtesy ConcernsThis critique makes two general types of criticismabout ‘safety’ concerns and ‘courtesy’ concerns.These themes recur throughout this critique, as Cand C++ have flaws that often compromise them.Safety concerns affect the external perception of thequality of the program; failure to meet them resultsin unfulfilled requirements, unsatisfied customersand program failures.

Courtesy concerns affect the internal view of the quality of a program in the development andmaintenance process. Courtesy concerns are usuallystylistic and syntactic, whereas safety concerns aresemantic. The two often go together. It is a courtesyconcern for an airline to keep its fleet clean and well

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 12/63

C++?? 9

3rd Edition © Ian Joyner 1996

maintained, which is also very much a safetyconcern.

Courtesy issues are even more important in thecontext of reusable software. Reusability depends onthe clear communication of the purpose of a module.Courtesy is important to establish socialinteractions, such as communication. Courtesyimplies inconvenience to the provider, but provides

convenience to others. Courtesy issues includechoosing meaningful identifiers, consistent layoutand typography, meaningful and non-redundantcommentary, etc. Courtesy issues are more than justa style consideration: a language design shoulddirectly support courtesy issues. A language,however, cannot enforce courtesy issues, and it isoften pointed out that poor, discourteous programscan be written in any language. But this is no reasonfor being careless about the languages that wedevelop and choose for software development.

Programmers fulfilling courtesy and safetyconcerns provide a high quality service fulfillingtheir obligations by providing benefits to other

programmers who must read, reuse and maintain thecode; and by producing programs that delight theend-user.

The programming by contract model has beenadvocated in the last few years as a model forprogramming by which safety and courtesy concernscan be formally documented. Programming bycontract documents the obligations of a client andthe benefits to a provider in preconditions; and thebenefits to the client and obligations of the providerin postconditions [Meyer 88], [Kilov and Ross 94].

2.10 Implementation and DeploymentConcerns

Class implementors are concerned with theimplementation of the class. Clients of the classonly need to know as much information about theclass as is documented in the abstract interface. Theimplementation is otherwise hidden.

Another aspect that is just as important to shieldprogrammers from is deployment concerns.Deployment is how a system is installed on theunderlying technology. If deployment issues arebuilt into a program, then the program lacksportability, and flexibility. One kind of deploymentconcern is how a system is mapped to the availablecomputing resources. For example, in a distributedsystem, this is what parts of the system are run inwhich location. As things can move around adistributed system, programmers should not buildinto their code location knowledge of other entities.Locations should be looked up in a directory.

Another deployment issue is how individualunits of a system are plugged together to form anintegrated whole. This is particularly important inOO, where several libraries can come from differentvendors, but their combination results in conflicts. Asolution to this is some kind of language that bindsthe units. Thus if you purchase two OO libraries,

and they have clashes of any kind, you can resolvethis deployment issue without having to change thelibraries, which you might not be able to do anyway.

Programmers should not only be separated fromimplementation concerns of other units, butseparated from deployment concerns as well.

2.11 Concluding RemarksIt is relevant to ask if grafting OO concepts onto aconventional language realises the full benefits of OO? The following parable seems apt: “No onesews a patch of unshrunk cloth on to an oldgarment; if he does, the patch tears away from it, thenew from the old, and leaves a bigger hole. No oneputs new wine into old wineskins; if he does, thewine will burst the skins, and then wine and skinsare both lost. New wine goes into fresh skins.” Mark 2:22

We must abandon disorganised and error-pronepractices, not adapt them to new contexts. How wellcan hybrid languages support the sophisticatedrequirements of modern software production? In myexperience bolt-on approaches to object-orientationusually end in disaster, with the new tearing awayfrom the old leaving a bigger hole.

Surely a basic premise of object-orientedprogramming is to enable the development of sophisticated systems through the adoption of thesimplest techniques possible? Software developmenttechnologies and methodologies should not impedethe production of such sophisticated systems.

3. C++ Specific Criticisms

3.1 Virtual Functions

This is the most complicated section in the critique,due to C++’s complex mechanisms. Although thisissue is central as polymorphism is a key concept of OOP, feel free to skim if you want an overview,without the details.

In C++ the keyword virtual enables thepossibility for a function to be polymorphic when itis overridden (redefined) in one or more descendantclasses, but the virtual keyword is unnecessary,as any function which is redefined in a descendantclass could be polymorphic. A compiler only needsto generate dynamic dispatch for truly polymorphicroutines.

The problem in C++ is that if a parent class

designer does not foresee that a descendant classmight want to redefine a function, then thedescendant class cannot make the functionpolymorphic. This is a most serious flaw in C++because it reduces the flexibility of softwarecomponents and therefore the ability to writereusable and extensible libraries.

C++ also allows functions to be overloaded, inwhich case the correct function to call depends onthe arguments. The actual arguments in the functioncall must match the formal arguments of one of theoverloaded functions. The difference between

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 13/63

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 14/63

C++?? 11

3rd Edition © Ian Joyner 1996

designer cannot prevent A::nonvirt from beingcalled. Objects of class B have their own specialisednonvirt , but B’s designer does not have controlover B’s interface to ensure that the correct versionof nonvirt is called.

C++ also does not protect class B from otherchanges in the system. Suppose we need to write aclass C that needs nonvirt to be virtual . Then

nonvirt in A will be changed to virtual . Butthis breaks the B::nonvirt trick. Therequirement of class C to have a virtual functionforces a change in the base class, which affects allother descendants of the base class, instead of thespecific new requirement being localised to the newclass. This is against to the reason for OOP havingloosely coupled classes, so that new requirements,and modifications will have localised effects, andnot require changes elsewhere which can potentiallybreak other existing parts of the system.

Another problem is that statements shouldconsistently have the same semantics. Thepolymorphic interpretation of a statement like

a->f() is that the most suitable implementation of f() is invoked for the object referred to by ‘a’,whether the object is of type A, or a descendent of A.In C++, however, the programmer must knowwhether the function f() is defined virtual or non-virtual in order to interpret exactly what a->f()means. Therefore, the statement a->f() is notimplementation independent and the principle of implementation hiding is broken. A change in thedeclaration of f() changes the semantics of theinvocation. Implementation independence meansthat a change in the implementation DOES NOTchange the semantics, of executable statements.

If a change in the declaration changes the

semantics, this should generate a compiler detectederror. The programmer should make the statementsemantically consistent with the changeddeclaration. This reflects the dynamic nature of software development, where you’ll see perpetualchange in program text.

For yet another case of the inconsistentsemantics of the statement a->f() vs constructors,consult section 10.9c, p 232 of the C++ ARM.Neither Eiffel nor Java have these problems. Theirmechanisms are clearer and simpler, and don’t leadto the surprises of C++. In Java, everything isvirtual , and to gain the effect where a methodmust not be overridden, the method may be defined

with the qualifierfinal

.Eiffel allows the programmer to specify aroutine as frozen , in which case the routine cannotbe redefined in descendants.

Option 2Using the function as is or overriding it should beleft open for the programmers of descendant classes.In C++, the possibility must be enabled in the baseclass by specifying virtual . In object-orienteddesign, the decisions you decide not to make are asimportant as the decisions you make. Decisions

should be made as late as possible. This strategyprevents mistakes being built into the system atearly stages. By making early decisions, you areoften stuck with assumptions that later prove to beincorrect; or the assumptions could be correct in oneenvironment, but false in another, making softwarebrittle and non-reusable.

C++ requires the parent class to specify potential

polymorphism by virtual (although an intermediateclass in the inheritance chain can introduce virtual).This prejudges that a routine might be redefined indescendants. This can be a problem because routinesthat aren’t actually polymorphic are accessed via theslightly less efficient virtual table technique insteadof a straight procedure call. (This is never a largeoverhead but object-oriented programs tend to usemore and smaller routines making routineinvocation a more significant overhead.) The policyin C++ should be that routines that might beredefined should be declared virtual. What is worseis that it says that non-virtual routines cannot beredefined, so the descendant class programmer has

no control.Rumbaugh et al put their criticism of C++’svirtual as follows: “C++ contains facilities forinheritance and run-time method resolution, but aC++ data structure is not automatically object-oriented. Method resolution and the ability tooverride an operation in a subclass are onlyavailable if the operation is declared virtual in thesuperclass. Thus, the need to override a methodmust be anticipated and written into the origin classdefinition. Unfortunately, the writer of a class maynot expect the need to define specialised subclassesor may not know what operations will have to beredefined by a subclass. This means that thesuperclass often must be modified when a subclassis defined and places a serious restriction on theability to reuse library classes by creating sub-classes, especially if the source code library is notavailable. (Of course, you could declare alloperations as virtual, at a slight cost in memory andfunction-calling overhead.)” [RBPEL91]

Virtual, however, is the wrong mechanism forthe programmer to deal with. A compiler can detectpolymorphism, and generate the underlying virtualcode, where and only where necessary. Having tospecify virtual burdens the programmer with anotherbookkeeping task. This is the main reason why C++is a weak object-oriented language as theprogrammer must constantly be concerned with lowlevel details, which should be automatically handledby the compiler.

Another problem in C++ is mistaken overriding.The base class routine can be overriddenunwittingly. The compiler should report anerroneous name redefinition within the same namespace unless the descendant class programmerspecifies that the routine redefinition is reallyintended. The same name can be used, but the pro-grammer must be conscious of this, and state thisexplicitly, especially in environments where systems

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 15/63

C++?? 12

3rd Edition © Ian Joyner 1996

are assembled out of preexisting components.Unless the programmer explicitly overrides theoriginal name a syntax error should report that thename is a duplicate declaration. C++, however,adopted the original approach of Simula. Thisapproach has been improved upon, and otherlanguages have adopted better, more explicitapproaches, that avoid the error of mistaken

redefinition.The solution is that virtual should not bespecified in the parent. Where run-time polymorphicdynamic-binding is required, the child class shouldspecify override on the function. When compile-time static-binding is required, the child class shouldspecify overload on the function. This has theadvantages: in the case of polymorphic functions,the compiler can check that the function signaturesconform; and in the case of overloaded functionsthat the function signatures are different in somerespect. The second advantage would be that duringthe maintenance phases of a program, the originalprogrammer’s intention is clear. As it is, later

programmers must guess if the original programmerhad made some kind of error in choosing a duplicatename, or whether overloading was intended.

In Java, there is no virtual keyword; allmethods are potentially polymorphic. Java usesdirect call instead of dynamic method lookup whenthe method is static , private or final . Thismeans that there will be non-polymorphic routinesthat must be called dynamically, but the dynamicnature of Java means further optimisation is notpossible.

Eiffel and Object Pascal cater for this option asthe descendant class programmer must specify thatredefinition is intended. This has the extra benefit

that a later reader or maintainer of the class caneasily identify the routines that have been redefined,and that this definition is related to a definition in anancestor class without having to refer to ancestorclass definitions. Thus option 2 is exactly where itshould be, in descendant classes.

Both Eiffel and Object Pascal optimise calls:they only generate dispatch table entries for dynamicbinding where a routine is truly polymorphic. Howthis is possible is covered in the section on globalanalysis.

Option 3The pure virtual function caters for leaving a

function abstract, that is a descendent class mustprovide its implementation if it is to be instantiated.Any descendants that do not define the routine arealso abstract classes. This concept is correct, but seethe section on pure virtual functions forcriticism of the terminology and syntax.

Java also has abstract methods, and in Eiffel, theimplementation is marked as deferred .SummaryThe main problem with virtual is that it forcesthe base class designer to guess that a function

might be polymorphic in one or more derivedclasses. If this requirement is not foreseen, or notincluded as an optimisation to avoid dynamicallydispatched calls, the possibility is effectively closed,rather than being left open. As implemented in C++,virtual coupled with the independent notion of overloading make an error prone combination.

Virtual is a difficult notion to grasp. The

related concepts of polymorphism and dynamicbinding, redefinition, and overriding are easier tograsp, being oriented towards the problem domain.Virtual routines are an implementation mechanismwhich instruct the compiler to set up entries in theclass’s virtual table; where global analysis is notdone by the compiler, leaving this burden to theprogrammer. Polymorphism is the ‘what’, andvirtual is the ‘how’. Smalltalk, Objective-C, Java,and Eiffel all use a different mechanism toimplement polymorphism.

Virtual is an example of where C++ obscures theconcepts of OOP. The programmer has to come toterms with low level concepts, rather than the higher

level object-oriented concepts. Virtual leavesoptimisation to the programmer. Other approachesleave the optimisation of dynamic dispatch to thecompiler, which can remove 100% of cases wheredynamic dispatch is not required. Interesting asunderlying mechanisms might be for the theoreticianor compiler implementor, the practitioner should notbe required to understand or use them to make senseof the higher level concepts. Having to use them inpractice is tedious and error-prone, and can preventthe adaptation of software to further advances in theunderlying technology and execution mechanisms(see concurrent programming), and reduces theflexibility and reusability of the software.

3.2 Global Analysis[P&S 94] note that there are two world assumptionsabout type safety. The first is the closed-world assumption, where all parts of the program areknown at compilation time, and type checking isdone for the entire program. The second is the open-world assumption, where type checking is doneindependently for each module. The open-worldassumption is useful when developing andprototyping. However, “When a finished producthas matured, it makes sense to adopt the closed-world assumption, since it enables more advancedcompilation techniques. Only when the entire

program is known, is it possible to perform globalregister allocation, flow analysis, or dead codedetection.” [P&S 94].

One of the major problems with C++ is the wayanalysis is divided between the compiler, whichworks under the open-world assumption, and thelinker which is depended on to do very limitedclosed-world analysis. Closed-world or globalanalysis is essential for two reasons: firstly, toensure that the assembled system is consistent; andsecondly to remove burden from the programmer byproviding automatic optimisations.

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 16/63

C++?? 13

3rd Edition © Ian Joyner 1996

The main burden that can be removed from theprogrammer is that of a base class designer havingto help the compiler build class virtual tables withthe virtual function modifier. As explained in thesection on virtual functions, this adversely effectssoftware flexibility. Virtual tables should not bebuilt when a class is compiled: rather virtual tablesshould only be built when the entire system is

assembled. During the system assembly (linker)phase, the compiler and linker can entirelydetermine which functions need virtual table entries.Other burdens are that the programmer must useoperators to help the compiler with information inother modules it cannot see, and the maintenance of header files.

In Eiffel and Object Pascal, global analysis of the entire system is done to determine the trulypolymorphic calls and accordingly construct thevirtual tables. In Eiffel this is done by the compiler.In Object Pascal, Apple extended the linker toperform global analysis. Such global analysis isdifficult in a C/Unix style environment, so in C++ it

was not included, leaving this burden to theprogrammer.In order to remove this burden from the

programmer, global analysis should have been putin the linker. However, as C++ was originallyimplemented as the Cfront preprocessor, necessarychanges to the linker weren’t undertaken. The earlyimplementations of C++ were a patchwork, and thishas resulted in many holes. The design of C++ wasseverely limited by its implementation technology,rather than being guided by the principles of betterlanguage design, which would require dedicatedcompilers and linkers. That is, C++ has beenseverely limited by its original experimental

implementation.I am now convinced that such technologydependence has severely damaged C++ as an object-oriented language and as a high level language. Ahigh level language removes the bookkeepingburden from the programmer and places them in thecompiler, which is the primary aim of high levellanguages. Lack of global or closed-world analysisis a major deficiency of C++, which leaves C++substantially lacking when compared to languagessuch as Eiffel. As Eiffel insists on system levelvalidity and therefore global analysis, it means thatEiffel implementations are more ambitious thanC++ implementations, and this is a major reasonwhy Eiffel implementations have been slower toappear.

Java dynamically loads pieces of software andlinks them into a running system as required. Thusstatic compile-time global analysis is not possible,as Java is designed to be dynamic. However, Javahas made the valid assumption that all methods arevirtual. This is one reason why Java and Eiffel aresubstantially different tools, although Eiffel hasrecently introduced Dynamic Linking in Eiffel(DLE).

3.3 Type-safe linkageThe C++ ARM explains that type-safe linkage is not100% type safe. If it is not 100% type-safe, then it isunsafe. Statistical analysis showed that in theChallenger disaster, the probability against anindividual O-ring failure was .997. But in acombination of 6 this small margin for failurebecame significant, meaning the combination was

very likely to fail. In software, we often find strangecombinations cause failure. It is the primaryobjective of OO to reduce these strangecombinations.

It is the subtle errors that cause the mostproblems, not the simple or obvious ones. Oftensuch errors remain undetected in the system untilcritical moments. The seriousness of this situationcannot be underestimated. Many forms of transport,such as planes, and space programs depend onsoftware to provide safety in their operation. Thefinancial survival of organisations can also dependon software. To accept such unsafe situations is atbest irresponsible.

C++ type safe linkage is a huge improvementover C, where the linker will link a function f (p1,...) with parameters to any function f (), maybe onewith no or different parameters. This results infailure at run time. However, since C++ type safelinkage is a linker trick, it does not deal with allinconsistencies like this.

The C++ ARM summarises the situation asfollows - “Handling all inconsistencies - thusmaking a C++ implementation 100% type-safe -would require either linker support or a mechanism(an environment) allowing the compiler access toinformation from separate compilations.”

So why do C++ compilers (at least AT&T’s) not

provide for accessing information from separatecompilations? Why is there not a specialised linkerfor C++, that actually provides 100% type safety?C++ lacks the global analysis of the previoussection. Building systems out of preexistingelements is the common Unix style of softwareproduction. This implements a form of reusability,but not in the truly flexible and consistent manner of object-oriented reusability.

In the future, Unix might be replaced by object-oriented operating systems, that are indeed ‘open’ tobe tailored to best suit the purpose at hand. By theuse of pipes and flags, Unix software elements canbe reused to provide functionality that approximates

what is desired. This approach is valid and workswith efficacy in some instances, like small in-houseapplications, or perhaps for research prototyping,but is unacceptable for widespread and expensivesoftware, or safety critical applications. In the lastten years the advantages of integrated software havebeen acknowledged. Classic Unix systems don’tprovide those advantages. Integrated systems aremore ambitious, and place more demands on theirdevelopers, but this is the sort of software nowbeing demanded by end users. Systems that arecobbled together are unacceptable. Today the

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 17/63

C++?? 14

3rd Edition © Ian Joyner 1996

emphasis is on software component technologiessuch as the public domain OpenDoc or Microsoft’sOLE .

A further problem with linking is that differentcompilation and linking systems should usedifferent name encoding schemes. This problem isrelated to type-safe linkage, but is covered in thesection on ‘reusability and compatibility’.

Java uses a different dynamic linkingmechanism, which is well defined and does not usethe Unix linker. Eiffel does not depend on the Unixor other platform linkers to detect such problems.The compiler must detect these problems.

Eiffel defines system-level validity . An Eiffelcompiler is therefore required to perform closed-world analysis, and not rely on linker tricks. Youcan thus be sure that Eiffel programs are 100% typesafe. A disadvantage of Eiffel is that compilers havea lot of work to do. (The common terminology is‘slow’, but that is inaccurate.) This is overcome tosome extent by Eiffel’s melting-ice technology,where changes can be made to a system, and tested

without the need to recompile every time.To summarise the last two sections: global orclosed-world analysis is needed for two reasons:consistency checks and optimisations. This removesmany burdens from the programmer, and its lack isa great shortcoming of C++.

3.4 Function OverloadingC++ allows functions to be overloaded if thearguments in the signature are different types.Overloaded functions are different to polymorphicfunctions: for each invocation the correct function isselected at compile time; with polymorphicfunctions, the correct function is bound dynamicallyat run-time. Polymorphism is achieved by redefiningor overriding routines. Be careful not to confuseoverriding and overloading. Overloading ariseswhen two or more functions share a name. These aredisambiguated by the number and types of thearguments. Overloading is different to multipledispatching in CLOS, as multiple dispatching onargument types is done dynamically at run-time.

[Reade 89] points out the difference betweenoverloading and polymorphism. Overloading meansthe use of the same name in the same context fordifferent entities with completely differentdefinitions and types. Polymorphism though has onedefinition, and all types are subtypes of a principletype. C. Strachey referred to polymorphism asparametric polymorphism and overloading as ad hocpolymorphism. The qualification mechanism foroverloaded functions is the function signature.

Overloading can be useful as these examplesshow:

max (int, int);max (real, real);

This will ensure that the best max routine for thetypes int and real will be invoked. Object-

oriented programming, however, provides a varianton this. Since the object is passed to the routine as ahidden parameter (‘this’ in C++), an equivalent butmore restricted form is already implicitly includedin object-oriented concepts. A simple example suchas the above would be expressed as:

int i, j;real r, s;i.max (j);r.max (s);

but i.max (r) and r.max (j) result in compilationerrors because the types of the arguments do notagree. By operator overloading of course, these canbe better expressed, i max j and r max s, but minand max are peculiar functions that could accept twoor more parameters of the same type so they can beapplied to a arbitrarily sized list. So the most generalcode in Eiffel style syntax will be something like:

il: COMPARABLE_LIST [ INTEGER ]rl: COMPARABLE_LIST [ REAL]

i := il.maxr := rl.max

The above examples show that the object-orientedparadigm, particularly with genericity can achievefunction overloading, without the need for thefunction overloading of C++. C++, however, doesmake the notion more general. The advantage is thatmore than one parameter can overload a function,not just the implicit current object parameter.

Another factor to consider is that overloading isresolved at compile time, but overriding at run-time,so it looks as if overloading has a performanceadvantage. However, global analysis can determinewhether the min and max functions are at the end of the inheritance line, and therefore can call themdirectly. That is, the compiler examines the objects iand r , looks at their corresponding max function,sees that at that point no polymorphism is involved,and so generates a direct call to max . By contrast, if the object was n which was defined to be a

NUMBER which provided the abstract max functionfrom which REAL.max and INTEGER.max werederived, then the compiler would need to generate adynamically bound call, as n could refer to either a

INTEGER or a REAL.If it is felt that C++’s scheme of having

parameters of different types is useful, it should be

realised that object-oriented programming providesthis in a more restricted and disciplined form. Thisis done by specifying that the parameter needs toconform to a base class. Any parameter passed tothe routine can only be a type of the base class, or asubclass of the base class. For example:

A.f (B someB) {...};class B ...;class D : public B ...A a;

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 18/63

C++?? 15

3rd Edition © Ian Joyner 1996

D d;a.f (d);

The entity ‘d’ must conform to the class ‘B’, and thecompiler checks this.

The alternative to function overloading bysignature, is to require functions with differentsignatures to have different names. Names should bethe basis of distinction of entities. The compiler cancross check that the parameters supplied are correctfor the given routine name. This also results inbetter self-documented software. It is often difficultto choose appropriate names for entities, but it iswell worth the effort.

[Wiener 95] contributes a nice example on thehazards of virtual functions with overloading:

class Parent{

public:virtual int doIt (int v){

return v * v;}

};

class Child : public Parent{

public:int doIt (int v,

int av = 20){

return v * av;}

};

void main(){

int i;Parent *p = new Child();i = p->doIt(3);

}

What is the value in i after execution of thisprogram? One might expect 60, but it is 9 as thesignature of doIt in Child does not match thesignature in Parent . It therefore does not overridethe Parent doIt , merely overloads it, and thedefault is unusable.

Java also provides method overloading , whereseveral methods can have the same name, but havedifferent signatures.

The Eiffel philosophy is not to introduce a newtechnique, but to use genericity, inheritance andredefinition. Eiffel provides covariant signatures,which means the signatures of descendant routinesdo not have to match exactly, but they do have toconform, according to Eiffel’s strong typing scheme.

Eiffel uses covariance with anchored types toimplement examples such as max. The Vintage 95Kernel Library specifies max as:

max (other : like Current ): like Current

This says that the type of the argument to max mustconform to the type of the current class. Thereforeyou get the same effect by redefinition without theoverloading concept. You also get type checking tosee that the parameter conforms to the current

object. Genericity is also a mechanism thatovercomes most of the need for overloading.

3.5 The Nature of InheritanceInheritance is a close relationship providing afundamental OO way to assemble softwarecomponents, along with composition and genericity.Objects that are instances of a class are alsoinstances of all ancestors of that class. For effectiveobject-oriented design the consistency of thisrelationship should be preserved. Each redefinitionin a subclass should be checked for consistency withthe original definition in an ancestor class. Asubclass should preserve the requirements of anancestor class. Requirements that cannot bepreserved indicate a design error and perhapsinheritance is not appropriate. Consistency due toinheritance is fundamental to object-oriented design.C++’s implementation of non-virtual overloading,means that the compiler does not check for thisconsistency. C++ does not provide this aspect of object-oriented design.

Inheritance has been classified as ‘syntactic’inheritance and ‘semantic’ inheritance. Saake et aldescribe these as follows: “Syntactic inheritancedenotes inheritance of structure or methoddefinitions and is therefore related to the reuse of code (and to overriding of code for inheritedmethods). Semantic inheritance denotes inheritanceof object semantics, ie of objects themselves. Thiskind of inheritance is known from semantic datamodels, where it is used to model one object thatappears in several roles in an application.” [SJE 91].Saake et al concentrate on the semantic form of inheritance. Behavioural or semantic inheritanceexpresses the role of an object within a system.

Wegner, however, believes code inheritance tobe of more practical value. He classifies thedifference between syntactic and semanticinheritance as code and behaviour hierarchies [Weg91] (p43). He suggests these are rarely compatiblewith each other and are often negatively correlated.Wegner also poses the question of “How shouldmodification of inherited attributes be constrained?”Code inheritance provides a basis formodularisation. Behavioural inheritance providesmodelling by the ‘is-a’ relationship. Both are usefulin their place. Both require consistency checks thatcombinations due to inheritance actually makesense.

It seems that inheritance is most powerful in themost restrictive form of a semantics preserving

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 19/63

C++?? 16

3rd Edition © Ian Joyner 1996

relationship; a subclass should preserve theassumptions of ancestor classes.

Meyer [Meyer 96a and 96b] has also produced aclassification of inheritance techniques. In histaxonomy he identifies 12 uses of inheritance, all of which he finds useful. This analysis also gives agood idea of when inheritance can be used, andwhen it should not.

Software components are like jig-saw pieces.When assembling a jig-saw the shape of the piecesmust fit, but more importantly, the resulting picturemust make sense. Assembling software componentsis more difficult. A jig-saw is reassembling a picturethat was complete before. Assembling softwarecomponents is building a picture that has never beenseen before. What is worse, is that often the jig-sawpieces are made by different programmers, so whenthe whole system is assembled, the pictures must fit.

Inheritance in C++ is like a jig-saw where thepieces fit together, but the compiler has no way of checking that the resultant picture makes sense. Inother words C++ has provided the syntax for classes

and inheritance but not the semantics. Reusable C++libraries have been slow to appear, which suggeststhat C++ might not support reusability as well aspossible. By contrast Java, Eiffel and Object Pascalare packaged with libraries. Object Pascal went verymuch in hand with the MacApp applicationframework. Java has been released coupled with theJava API, a comprehensive library. Eiffel is alsointegrated with an extremely comprehensive library,which is even larger than Java’s. In fact the conceptof the library preceded Eiffel as a project toreclassify and produce a taxonomy of all commonstructures used in computer science. [Meyer 94].

3.6 Multiple InheritanceBoth Eiffel and C++ provide multiple inheritance.Java does not, claiming it results in many problems.Instead Java provides interfaces , which are similarto Objective C’s protocols. Sun claims interfacesprovide all the desirable features of multipleinheritance.

Sun’s claim that multiple inheritance results inproblems is true particularly in the way that C++ hasimplemented multiple inheritance. What seems likea simple generalisation of inheriting from multipleclasses instead of just one, turns out to be non-trivial. For example, what should be the policy if you inherit an item of the same name from twoclasses? Are they compatible? If so should they bemerged into a single entity? If not, how do youdisambiguate them? And so the list goes on.

Java’s interface mechanism implements multipleinheritance, with one important difference: theinherited interfaces must be abstract. This doesobviate the need to choose between differentimplementations, as with interfaces there are noimplementations. Java allows the declaration of constant fields in an interface. Where these aremultiply inherited, they merge to form one entity so

that no ambiguity arises, but what happens if theconstants have different values?

Since Java does not have multiple inheritance,you cannot do mixins as you can in C++ and Eiffel.Mixin is the ability to inherit sets of non-abstractroutines from different classes to build a newcomplex class. For example, you might want toimport utility routines from a number of different

sources. However, you can achieve the same effectusing composition instead of inheritance, so this isprobably not a great minus against Java.

Eiffel solves multiple inheritance problemswithout having to introduce a separate, interfacemechanism.

Some feel that single inheritance is elegant byitself, but that multiple inheritance is not. This isone particular standpoint.

BETA [Madsen 93] falls into the ‘multipleinheritance is inelegant’ category: “Beta does nothave multiple inheritance, due to the lack of aprofound theoretical understanding, and alsobecause the current proposals seem technically very

complicated.” They cite Flavors as a language thatmixes classes together, where according to Madsen,the order of inheritance matters, that is inheriting(A, B) is different from inheriting (B, A).

Ada 95 is also a language that avoids multipleinheritance. Ada 95 supports single inheritance asthe tagged type extension .

Others feel that multiple inheritance can provideelegant solutions to particular modelling problemsso is worth the effort. Although, the above list of questions arising from multiple inheritance is notcomplete, it shows that the problems with multipleinheritance can be systematically identified, andonce the problems are recognised, they can be

solved elegantly. While [Sakkinen 92] goes into theproblems of multiple inheritance in great depth, hedefends it.

Eiffel has taken the approach that multipleinheritance poses some interesting and challengingproblems, but rises to the challenge, and solves themelegantly. Nor does the order of inheritance matter.All resolutions that the programmer must specify aregiven in the inheritance clause of a class. Thisincludes renaming to ensure that multiple featuresinherited with the same name end up as multiplefeatures with unambiguous names, redefining , newexport policies for inherited features, undefining ,and disambiguating with select . In all cases, the

action taken by the compiler, whether using fork or join semantics is made clear, and the programmerhas complete control.

C++ has a different disambiguation mechanismto Eiffel. In Eiffel, one or both of the features mustbe given a different name in the renames clause. InC++ the members must be disambiguated using thescope resolution operator ‘::’. The advantage of theEiffel approach is that the ambiguity is dealt withdeclaratively in one place. Eiffel’s inheritance clauseis considerably more complex than C++’s, but thecode is considerably simpler, more robust and

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 20/63

C++?? 17

3rd Edition © Ian Joyner 1996

flexible, which is the advantage of the declarativeapproach as against the operator approach. In C++,you must use the scope resolution operator in thecode, every time you run into an ambiguity problembetween two or more members. This clutters thecode, and makes it less malleable, as if anythingchanges that affects the ambiguity, you potentiallyhave to change the code everywhere, where the

ambiguity occurs.According to [Stroustrup 94] section 12.8, theANSI committee considered renaming, but thesuggestion was blocked by one member whoinsisted that the rest of the committee go away andthink about it for two weeks. The example in section12.8 shows how the effect of renaming is achieved,without explicit renaming. The problem is, if it took this group of experts two weeks to work this out,what chance is there for the rest of us?

The scope resolution operator is used for morethan just multiple inheritance disambiguation. Sinceambiguities could be avoided by cleaner languagedesign, the scope resolution operator is an ugly

complication.The question of whether the order of declarationof multiple parents matters in C++ is complex. Itdoes affect the order in which constructors arecalled, and can cause problems if the programmerdoes really want to get low level. However, thiswould be considered poor programming practice.

Another difference between C++ and Eiffel isdirect repeated inheritance. Eiffel allows:

class B inherit A, A end

but

class B : public A, public A { };

is disallowed in C++.

3.7 Virtual ClassesThe meaning of the keyword virtual is quitedifferent when used in the context of a class to thecontext of a function: with a class it means thatmultiply inherited features are merged; with afunction it means polymorphism. Virtual class doesnot mean that members in the class are allpolymorphic. In fact the two uses of virtual actuallymean quite the opposite of each other: virtualfunctions mean that there could be more than onefunction; virtual classes mean that if the class ismultiply inherited, you only get a single copy.

C++ saves on keywords by overloading onekeyword in several contexts, even though the useshave different or even opposite meanings. Static isanother case, which is used in three differentcontexts. The keyword count metric does not showthat C++ is a small non-complex language: lesskeywords have made C++ more complex andconfusing.

So what do virtual classes do? If class Dmultiply inherits class A via classes B and C, then if D wants to inherit only a single shared copy of A,

the inheritance of A must be specified as virtualin both B and C. C++ virtual classes raise twoquestions. Firstly, what happens if A is declaredvirtual in only one of B or C? Secondly, what if another class E wants to inherit multiple copies of Avia B and C? In C++, the virtual class decision mustbe made early, reducing the flexibility that might berequired in the assembly of derived classes. In a

shared software environment different vendorsmight supply classes B and C. It should be left tothe implementor of class D or E, exactly how toresolve this problem. And this is the simplest case:what if A is inherited via more than two paths, withmore than two levels of inheritance? Flexibility iskey to reusable software. You cannot envisage whendesigning a base class all the possible uses inderived classes, and attempting to do soconsiderably complicates design.

As Java has no multiple inheritance, there is noproblem to be solved here.

The Eiffel mechanism allows two classes D andE inheriting multiple copies of A to inherit A in the

appropriate way independently. You do not have tochoose in intermediate classes whether A is virtual,ie., inherited as a single copy, or not. Theinheritance is more flexible and done on a feature byfeature basis, and each feature from A will eitherfork, in which it becomes two new features; or join,in which case there is only one resultant feature. Theprogrammer of each descendant class can decidewhether it is appropriate to fork or join each featureindependently of the other descendants, or anypolicy in A.

The fine grained approach of Eiffel is asignificant benefit over C++. While the Eiffelapproach is more sophisticated and flexible, the

syntax is far simpler, and the concepts are easier tounderstand.

3.8 TemplatesTemplates are C++’s mechanism to implement theconcept of genericity . Templates are much the sameas parameterised classes , which is the mechanismEiffel uses for genericity. Genericity is a majorfeature of Ada and Algol 68 and is a valuableaddition to C++. Some see genericity as a morefundamental software assembly mechanism thaninheritance, and certainly less problematic. Ada isan example where genericity is more fundamentalthan inheritance. In C++’s Standard Template

Library (STL), genericity is used almost exclusivelyinstead of inheritance. Meyer [Meyer 88] states thatgenericity is an essential part of an object-orientedlanguage. [P&S 94] see genericity as a mechanismthat achieves type substitution, which you cannot dowith inheritance. Thus genericity is essential as acomplementary concept to inheritance.

Genericity allows you to build collections of items, where the type of items is known, and itemscan be retrieved from the collection as that type,without type casting. In a language withoutgenericity you code a LIST class, and objects of any

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 21/63

C++?? 18

3rd Edition © Ian Joyner 1996

type can be added to lists. If the list is only forshopping items, it makes semantic nonsense to add aperson to the list. Without genericity there is nostatic type check to ensure you can’t add people toyour shopping list. You might be able to catch thisoccurrence at run time, but the advantage of statictyping is lost.

Without genericity you could code specific lists

for shopping items, people, and every other itemyou could put in lists. The basic functionality of alllists is the same, but you must duplicate effort, andmanually replicate code. That is you must duplicateeffort if you are going to preserve semantics and betype safe.

Languages such as Eiffel and C++ allow you todeclare a LIST of shopping items , so the compilercan ensure that you cannot add people to such a list.You can also easily add lists that contain any othertype of entity, just by a simple declaration. You donot have to manually replicate the basicfunctionality of the list for every type of elementyou are going to put in it.

This has lead to a criticism of the C++ templatemechanism that you get ‘code bloat’. That is forevery type based on a template definition thecompiler might replicate the code. Seeing that thepurpose of templates is to save the programmer frommanual replication, this does not seem like a badthing. A good implementation of C++ will avoid‘code bloat’ where possible. In fact it is allowed forin the C++ ARM: “This can cause the generation of unnecessarily many function definitions. A goodimplementation might take advantage of thesimilarity of such functions to suppress spuriousreplications.”

Thus I don’t criticise C++ as others have done

on the basis of ‘code bloat’. The whole concept of generics and templates is simple and yet powerful,and allows the generation of quite sophisticatedprograms from simple specifications. If you areoverly worried about ‘code bloat’, simply do not usegenericity. As [Stroustrup 94] points out “What youdon’t use, you don’t pay for.” This is a goodprinciple for compiler implementors. Many peoplewill use genericity though, as few will find itpractical to code a different kind of LIST for everypossible list element.

While the concept of genericity and templates iscorrect, there are several problems with templates inC++. The syntax leaves a lot to be desired. Readers

can of course form their own opinions of that.However, again C++ masks what is a simple andpowerful mechanism with complicated syntax, sopeople will baulk at using it. There are examples of where the quirky syntax is a trap for young players[Stroustrup 94]. For example, declaring a list of alist of integers would easily be notated:

List<List<int>> a;

However, this results in a syntax error as ‘>>‘ is theright shift or output operator. You must notate thisas ‘> >‘:

List<List<int> > a;

Further, “template” is confusing terminology, as theconceptual view is that a class is a template for a setof objects. “Object-oriented languages allow one todescribe a template, if you will, for an entire set of objects. Such a template is called a class.” [Ege 96].This is not the meaning of the C++ term template,which refers to genericity.

Another more serious problem is that there is noconstraint on the types that can be used as theparameters to the templates; the coder of a templateclass can make no assumptions about the type of thegeneric parameter. Thus the class coder cannot issuea function call from within the template class to thegeneric type without a type cast.

As the ARM says on this topic: “Specifying norestrictions on what types can match a typeargument gives the programmer the maximumflexibility. The cost is that errors - such asattempting to sort objects of a type that does nothave comparison operators - will not in general bedetected until link time.”

This shows the need for at least an optional typeconstraint on the actual types passed to the template.Eiffel has such optional constraints in the form of constrained genericity . For example:

class SORTED_LIST [T -> COMPARABLE ]...feature insert (item : T ) is ... end

end

ensures that the type of the item to insert hasappropriate comparison operators from typeCOMPARABLE in order to insert item in the rightplace in the SORTED_LIST . Note that multipleinheritance is important, so that any type eligible forinsertion in the SORTED_LIST includes thecomparison operators.

Java, alas has no genericity mechanism. TheJava recommendation is to use type casts when everretrieving an object from a container class [Flan 96].

[P&S 94] have a good chapter on genericity.Genericity is the ability to build a derived class froma base class by type substitution. Compare this withinheritance, where you can add class members andredefine inherited routines. They criticise theparameterised class/template mechanisms of Eiffel

and C++ for three reasons: firstly, there are twokinds of class, generic and non-generic; secondly,you can apply generic instantiation only once; andthirdly, a generic instance is not a subclass.

BETA uses a different mechanism, virtualbinding , which is more flexible than the Eiffel/C++parameterised classes, but [P&S 94] shows that youcan produce derived classes that are not staticallytype correct.

A significant problem with the parameterisedclass mechanism is that the base class designer must

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 22/63

C++?? 19

3rd Edition © Ian Joyner 1996

think about it in advance, and then only the typesnominated in the parameter list can be substituted.This reduces flexibility. [P&S 94] suggests agenericity mechanism known as class substitution ,which make inheritance and genericity orthogonalrather than independent concepts. Class substitutionhas the advantage that a base class designer does notneed to design genericity into the base class, any

subclass can perform class substitution; and anytype in the base class may be substituted, not onlythose given in the parameter list. Furthermore, classsubstitution can be applied repeatedly, whereasinstantiation of a parameterised class can be doneonly once.

An example of class substitution in Eiffel likesyntax is:

class Afeature x, y: T

assign isdo x := yend

end

This can be modified using class substitution:

A [T <- INTEGER ] A [T <- ANIMAL]

You can also use constrained genericity with exactlythe same syntax that Eiffel now has, as in theSORTED_LIST example, except that semanticallythe [T -> COMPARABLE ] only specifies that anyclass substituting T must be a subclass of COMPARABLE . [T -> COMPARABLE ] is not aparameter list though. You can build new types outof sorted list:

SORTED_LIST [T <- INTEGER ]SORTED_LIST [T <- STRING ]

Java might be in the best position to implement thisflexible class substitution mechanism for genericity,as it has not implemented genericity yet. Eiffel andC++ could extend their mechanisms, but then therewould be two ways of doing the same thing, exceptthe class substitution mechanism is more flexiblethan parameterised classes. I do not know of anylanguages that implement class substitution as yet,and other consequences must be thought throughbefore adding it to languages, so don’t dispose of your Eiffel and C++ compilers just yet!

3.9 Name OverloadingClear names are fundamental in producing self-documenting software helping to produce maintain-able and reusable software components. Names are

fundamental in freeing programmers from low levelmanipulation of addresses. Naming is the basis fordifferentiating between different entities in asoftware module. In programming, when we use theterm name, we usually mean identifier. To beprecise, a name is a label which can refer to morethan one entity, in which case the name isambiguous. An identifier is a name that

unambiguously identifies an entity. (To bemathematical, a name is a relation, an identifier is afunction.) Where a name is ambiguous, it needsqualification to form an identifier to the entity. Forexample, there could be two people named JohnDoe; to disambiguate the reference, you wouldqualify each as John Doe of Washington or JohnDoe of New York .

Name overloading allows the same name to referto two or more different entities. The problem withan ambiguous name is whether the resultantambiguity is useful, and how to resolve it, asambiguity weakens the usefulness of names todistinguish entities.

Name overloading is useful for two purposes.Firstly, it allows programmers to work on two ormore modules without concern about name clashes.The ambiguity can be tolerated as within the contextof each module the name unambiguously refers to aunique entity; the name is qualified by itssurrounding environment. Secondly, nameoverloading provides polymorphism, where thesame name applied to different types refers todifferent implementations for those types.Polymorphism allows one word to describe ‘what’ iscomputed. Different classes might have differentimplementations of ‘how’ a computation is done.For example ‘draw’ is an operation that is applicableto all different shapes, even though circles andsquares, etc., are ‘drawn’ differently.

These two uses of name overloading provide apowerful concept. The use of the same name in thesame context must be resolved. Errors can resultfrom ambiguity, in which case the programmer mustdifferentiate between entities with some form of qualification of the name. A common way to do thisis to introduce extra distinguishing names. Forexample, in a group of people where two or moreshare the same first name, they can be distinguishedby their surname. Similarly a unique first name willdistinguish the members of a family with a commonsurname.

This is analogous to classes, where each class ina system is given a unique name. Each memberwithin a class is also given a unique name. Wheretwo objects with members of the same name areused within the same context, the object name canqualify the members. In this case the dot operatoracts as a qualifier, for example, a.mem and b.mem.

Locals in a recursive environment are anexample of ambiguity which is resolved at run-time.A single local identifier in the static text of afunction can refer to many entities. When thefunction is called recursively, the name is qualified

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 23/63

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 24/63

C++?? 21

3rd Edition © Ian Joyner 1996

shadowed variables, a variable named x in asuperclass can be hidden from the current class byanother variable named x. You can still access bothvariables by the use of this.x and super.x , which arethe equivalents of scope resolution. The ambiguityproblem would have been better avoided altogetherby reporting a duplicate identifier.

Eiffel also has no globals, so a construct such as

namespaces is not needed. Eiffel does not allowname clashes: you must either change the name of one of the entities, or when combining classes withinheritance, use a rename clause. With this schemethere is no need for scope resolution or ‘super’operators, making the imperative part of thelanguage simpler, by using declarative techniques.

3.10 Nested ClassesSimula provided textually nested classes similar tonested procedures in ALGOL. Textual (syntactic)nesting should not be confused with semanticnesting, nor static modelling with dynamic run-timenesting. Modelling is done in the semantic domain,and should be divorced from syntax; you do notneed textually nested classes to have nested objects.Nested classes are contrary to good object-orienteddesign, and the free spirit of object-orienteddecomposition, where classes should be looselycoupled, to support software reusability.

Instead of tightly coupled environments:

A

B

C

.

.

Z

You should decouple depending on the modellingrequirements:

A

B Binherit A a: A

or

C Cinherit A a: A

. .

. .

Z Zinherit A a: A

is-a component-of/ related-to

This is a more flexible arrangement, both in terms of modelling and program maintenance.

There are two problems with nested classes:firstly, the inner class is dependent on the outerclass, and so is not reusable, contrary to goodobject-oriented design, where classes areindependent; secondly, the inner class has access tothe implementation of the outer class, soimplementation hiding is violated. Where access toa class’s implementation is needed, you should useinheritance, but note this models the is-arelationship, not the component-of relationship thatnested classes do.

Semantic nesting is achieved independently of textual nesting. In object-oriented design all objectsshould interact only via well defined interfaces, butobjects of a class that is textually nested in anotherclass have access to the outer object without thebenefit of a clean interface. C avoided the

complexity of nested functions, but C++ has chosento implement this complexity for classes, which isof less use than nested functions, and is contrary togood object-oriented design.

Pascal and ALGOL programmers sometimes usenested procedures in order to group things together,but nested procedures are not necessary, and if youwant to use a nested procedure in anotherenvironment, you have to dig it out of where it isand make it global, which is a maintenance problem.If the procedure uses locals from the outerenvironment, you have more problems. You will

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 25/63

C++?? 22

3rd Edition © Ian Joyner 1996

have to change these to parameters, which is acleaner approach anyway, and you will probablyhave to unindent all the text by one or more levels.Textually nested classes have worse problems.

Semantically, OOP achieves nesting in twoways: by inheritance and object-orientedcomposition. Modelling nesting is achieved withouttight textual coupling. Consider a car. In the real

world the engine is embedded in the car, but inobject-oriented modelling embedding is modelledwithout textual nesting. Both car and engine areseparate classes: the car contains a reference to anengine object. This allows the vehicle and enginehierarchy to be independently defined. Engine isderived independently into petrol, diesel, andelectric engines. This is simpler, cleaner and moreflexible than having to define a petrol engine car, adiesel engine car, etc., which you have to do if youtextually nest the engine class in the car. In the realworld you can change the cars engine, so it does noteven make sense to tightly couple the car and theengine.

In C++, not only can classes be nested withinother classes, but also within functions, therebytightly coupling a class to a function. This confusesclass definition with object declaration. The class isthe fundamental structure in object-orientedprogramming and nothing has existence separatefrom a class (including globals).

Neither Java, nor Eiffel provide nested classes,and yet everything you can model in C++, you canalso model in these languages, without the problemsassociated with textual nesting.

Chapter 18 of [Madsen 93] provides very goodinsights about modelling; classification andcomposition are the means to organise complexity in

terms of hierarchies. [Madsen 93] enumerates fourkinds of composition: whole-part composition,reference composition, localisation, and conceptcomposition. They say that these are not altogetherindependent as one composition relationship couldfall into two or more categories. Whole-partcomposition models the car example above, wherethe engine is part of the car. Reference compositionis illustrated where a person makes a hotelreservation. The person is not a part of thereservation, but the reservation references theperson. [Madsen 93] can be consulted for definitionsof localisation and concept composition.

As examples can be given of composition that

can be modelled in terms of more than one of thecategories of composition, it is better not to providedirect modelling of this in the programminglanguage; your opinion might later change. BETAdoes have mechanisms for modelling the whole-partcomposition as embedded objects, and reference asreferences. However, this is quite different to textualnesting. There is no real need to support thesedifferent categories in your programming language.It is more important for the analyst to be cogniscentof these different flavours so that he can recognise

different kinds of composition in the problemdomain.

3.11 Global EnvironmentsThere are two important properties of globals:firstly, a global is visible to the whole program,which is a compile-time view; and secondly, aglobal is active for the entire execution of aprogram, which is a run-time property. The firstproperty is not desirable in the object-orientedparadigm, as will be explained below. The secondproperty can easily be provided. The life of anyentity is the life of the enclosing object, so to haveentities that are active for the whole execution of theprogram, you create some objects when the programstarts, which don’t get deallocated until the programcompletes.

The global environment provides a special caseof nested classes. When classes are nested in aglobal environment, dependencies can arise thatmake the classes difficult to decouple from theoriginal program, and therefore not reusable, by

themselves. You might be forced to relocate a largeamount of the global environment as well. There arealso problems with the related mechanisms of header files and namespaces. Even if a class is notintended for use in another context, it will benefitfrom the discipline of object-oriented design. Eachclass is designed independently of the surroundingenvironment, and relationships and dependenciesbetween classes are explicitly stated.

In C++ functions can change the globalenvironment, beyond the object in which they areencapsulated. Such changes are side-effects thatlimit the opportunity to produce loosely-coupledobjects, which is essential to enable reusable

software. This is a drawback of both global andnested environments. A good OO language will onlypermit routines in an object to change its state.

Removing the global environment is trivial:simply encapsulate it in an object or set of objects.The previously global entities are then subject to thediscipline of object-oriented design; globalscircumvent OOD. Objects can also provide a cleaninterface to the external environment, or operatingsystem, without loss of generality, for a negligibleperformance penalty. Classes are independent of thesurrounding environment, and the project for whichthey were first developed, and are more easilyadaptable to new environments and projects.

Java has removed globals from the languagealtogether. Eiffel is another example of a languagewhere there are no globals. Both these languagesshow that globals are not needed for, and evendetrimental to the development of large computersystems.

In concurrent and distributed environments youare better off without globals. In a distributedenvironment, the global state of the system may beimpossible to determine. In order to developdistributed systems, you cannot have globals.Similarly with concurrent environments, problems

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 26/63

C++?? 23

3rd Edition © Ian Joyner 1996

arise when two or more process threads accessshared resources at the same time. Shared resourcesshould only be accessed via an object whichmanages the resource, and prevents contention forthe shared resource. Such a resource should not be aglobal.

3.12 Polymorphism and Inheritance

Inheritance provides a textually decoupled form of subblock. The scope of a name is the class in whichit occurs. If a name occurs twice in a class, it is asyntax error. Inheritance introduces some questionsover and above this simple consideration of scope.Should a name declared in a base class be in scopein a derived class? There are three choices:

1) Names are in scope only in the immediateclass but not in subclasses. Subclasses can freelyreuse names because there is no potential for a clash.This precludes software reusability. Since subclasseswill not inherit definitions of implementation, case 1is not worth considering.

2) The name is in scope in a subclass, but thename can be overloaded without restriction. This isclosest to the overloading of names in nested blocks.This is C++’s approach. Two problems arise: firstly,the name can be reused so the inherited entity isunintentionally hidden; secondly, because the newentity is not assumed to have any relationship to theoriginal, its signature cannot be type checked withthe original entity. Since consistency checksbetween the superclass and subclass are notpossible, the tight relationship that inheritanceimplies, which is fundamental to object-orienteddesign, is not enforced. This can lead toinconsistencies between the abstract definition of abase class, and the implementation of a derived

class. If the derived class does not conform to thebase class in this way, it should be questioned whythe derived class is inheriting from the base class inthe first place. (See the nature of inheritance.)

3) The name is in scope in the subclass, but canonly be overridden in a disciplined way to provide aspecialisation of the original. Other uses of the nameare reported as duplicate name errors. This form of overriding in a subclass ensures the entity referred toin the subclass is closely related to the entity in theancestor class. This helps ensure design consistency.The relationship of name scope is not symmetric.Names in a subclass are not in scope in a superclass(although this is not the case in dynamically typed

languages such as Smalltalk). In order to provide theconsistent customisation of reusable softwarecomponents, the same name should only be usedwhen explicitly redefining the original entity. Theprogrammer of the descendant class should indicatethat this is not a syntax error due to a duplicatename, but that redefinition is intended, (thesuggested keyword override has already beencovered in the virtual section.) This choice ensuresthat the resultant class is logically constructed. Thismight seem restrictive, but is analogous to strong

typing, and makes inheritance a much morepowerful concept.

3.13 Type Casts“Syntactically and semantically, casts are one of theugliest features of C and C++.” not my words or anyother detractor of C++, but from [Stroustrup 94].

Mathematical functions map values from onetype to values of another type. For examplearithmetic multiplication maps the type ‘pair of integers’ to an integer:

Mult : INTEGER x INTEGER -> INTEGER

A language type system enables a programmer tospecify which mappings make sense. Like functions,type casts map values of one type onto values of another type, but this forces one type to another,against the defined mappings, undermining thevalue of the type system. A strongly typed languagewith a well defined type system does not need casts:all type to type mapping is achieved with functionsthat are defined within the type system; no castsoutside the type system are needed.

Type casts have been useful in computer sys-tems. Sometimes it is required to map one type ontoanother, where the bit representation of the valueremains the same. Type casts are a trick to optimisecertain operations, but provide no useful conceptthat general functions don’t provide. In manylanguages, the type system is not consistentlydefined, so programmers feel that type casts arenecessary, or the language would be restrictive.

An example often used in programming is tocast between characters and integers. Type castsbetween integers and characters are easily expressedas functions using abstract data types (ADTs).

TYPE CHARACTER

FUNCTIONS ord : CHARACTER -> INTEGER

// convert input character to integerchar : INTEGER /-> CHARACTER

// convert input integer to character

PRECONDITION // check i is in range

pre char (i: INTEGER ) =

0 <= i and i <= ord (last character )The notation ‘->’ means every character will map toan integer. The partial function notation ‘/->’ meansthat not every integer will map to a character, and aprecondition, given in the pre char statement,specifies the subset of integers that maps tocharacters. Object-oriented syntax provides thisconsistently with member functions on a class:

i: INTEGERch : CHARACTER

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 27/63

C++?? 24

3rd Edition © Ian Joyner 1996

i := ch .ord // i becomes the integer value of the character.ch := i.char // ch becomes the character corresponding to i.

but a routine char would probably not be defined onthe integer type so this would more likely be:

ch .char (i) // set ch to the character corresponding to i.

The hardware of many machines cater for such basicdata types as character and integer, and it is probablethat a compiler will generate code that is optimal forany target hardware architecture. Thus many lan-guages have characters and integers as built in types.An object-oriented language can treat such basicdata types consistently and elegantly, by the implicitdefinition of their own classes.

Another example of type conversion is from realto integer; but there are several options. Do youtruncate or round?

TYPE REAL

FUNCTIONStruncate : REAL -> INTEGERround : REAL -> INTEGER

r : REALi: INTEGER

i := r .truncate // i becomes the closest integer // <= r i := r .round

// i becomes the closest integer to r Again many hardware platforms provide specificinstructions to achieve this, and an efficient object-oriented language compiler will generate code bestoptimised for the target machine. Such inbuilt classdefinitions might be a part of the standard languagedefinition.

3.14 RTTI and Type castsSince the second edition of this critique in 1992,C++ added Run-Time Type Information (RTTI) inMarch 1993. This is a good and necessary feature,and a discussion of it helps clarify the notion of casts.

[P&S 94] makes a case against rejecting allprograms that are not statically type correct. If aprogram is shown to be statically type correct, itstype correctness is guaranteed , but static typechecks can reject a class of programs that areotherwise type valid.

List classes are an example of where static typechecking can reject a valid program. A list class cancontain objects of many different types. Genericityand templates allow constructions such as list of

objects , list of animals , etc. These are types builtfrom the generic list class.

In the list of animals, you might know thatsquirrels occur in even numbered slots in the list.You could then assign an even numbered listelement to a variable of type squirrel. Dynamically,this is correct, but statically the compiler must rejectit as it does not know that only squirrels occur in

even locations in the list.Things aren’t always this simple. Theprogrammer probably won’t know the pattern of how particular animals are stored in the list.Consider a vet’s waiting room. The vet might viewhis waiting room as being the type: list of animals .Calling in the first animal from the waiting room, itis important to know whether the animal is a cat or ahamster if the vet is to perform an operation on theanimal. For many such cases object-orienteddynamic binding and polymorphism will suffice, sothat the programmer does not have to know theexact type of the object, as long as the objects aresufficiently the same that the same operations can be

applied, even though the implementations might bedifferent.However, this is not always sufficient, and

sometimes it is important to know that you haveretrieved a hamster from a list of animals.

For example, once our vet has performed theoperation on the hamster or cat, he must knowenough about their type to decide whether to nowput the animal in the hamster cage, or the cat basket.

Casting can solve this problem, but it is asledgehammer approach where much more elegantand precise solutions exist. [Stroustrup 94] notes:“The C and C++ cast is a sledgehammer.”

Eiffel has such an elegant and precise solution

called the assignment attempt , notated as ‘?=‘instead of ‘:=‘. A simple example is:

waiting_room : LIST [ ANIMAL] fluffy: HAMSTERh_cage : HAMSTER_CAGE

fluffy := waiting_room.first -- error.-- The above assignment will be rejected by the-- compiler as type ( fluffy) = HAMSTER and -- ANIMAL is not a subtype of HAMSTER. Even-- though we know that the animal will be a-- HAMSTER , and the program is valid, static

-- type checking considers it invalid. fluffy ?= waiting_room.first

-- If the first animal in the waiting room is-- indeed a HAMSTER , then fluffy will refer-- to that animal, else fluffy will be Void.

if fluffy /= Void thenh_cage.put ( fluffy)

end

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 28/63

C++?? 25

3rd Edition © Ian Joyner 1996

The Eiffel assignment attempt provides a precise andelegant solution to the dynamic type problem. Sincethe assignment attempt has the desired effect of by-passing static type checking and leaving it to runtime, type casting is not needed.

If you want to be as flexible as Smalltalk, youcould use assignment attempt instead of straightassignment everywhere, but as this invokes run time

type checks, and you must check for Void references, there is a large overhead to assignmentattempt over straight assignment. This shows thatnot only is static typing important for provingcompile-time correctness, but also for run-timeefficiency. The only real effect of ?= as far as theprogrammer is concerned is that it suppresses thecompiler’s static type checking and puts in a run-time check.

As I said, C++ introduced Run-Time TypeInformation (RTTI) in March 1993. RTTI has theoperator dynamic_cast , which achieves thesame effect as the Eiffel assignment attempt.dynamic_cast returns a pointer to a derived

class from a pointer to a base class if the object is anobject of the derived class; otherwise it returns 0 (orshould that be null? But 0 isn’t really zero, but anybit pattern representing null).

In C++, the above assignment attempt would becoded:

fluffy =dynamic_cast<hamster*>

(waiting_room.first());

A few observations. Wow! Eiffel uses an operator,and C++ uses a keyword. It should be noted thoughthat in correctly designed programs, neitherassignment attempt, nor dynamic_cast will beused very often. So this is a small point.The second observation is that in C++ you mustspecify the type. In this example it is superfluous asthe compiler can determine type ( fluffy) =

HAMSTER , as it does in Eiffel.In C++ you can dynamically cast to any derived

class from hamster* but that does not seem togain anything. A second point is that you don’t needto use dynamic_cast directly in an assignment,but can use it in a general expression. However,again it is stressed that run time casting should be solittle used that this is of little advantage. Perhaps theonly small advantage is the ability to be able to passa dynamically cast pointer:

h_cage.put(dynamic_cast<hamster*>

(waiting_room.first());

Looks good right? But remember, if the first animalout of the waiting room is not a hamster, but a rat,you get 0 (well null...etc) returned which will causeh_cage.put() to fail.

This shows that the use of dynamic_cast inan expression is not such a good idea, as it mightcause the whole expression to fail.

Thus Eiffel’s assignment attempt is safer andsyntactically cleaner. And there is another reason forthis remark: if you don’t put the if fluffy /= Void then test in, either deliberately or because youforgot, then the precondition that is most likely inthe Eiffel version of h_cage . put tests that theargument is not Void. If you deliberately left out theVoid test, you will have included a rescue clause to

handle this exception.Although the Eiffel syntax ‘?=‘ for assignmentattempt is cleaner, [Stroustrup 94] points out thatsuch clean syntax would be inappropriate for C++.This is because the ‘?=‘ would be “difficult to spot”in C++’s otherwise clumsy syntax. This is why it ispossible to use this neat notation in Eiffel, asEiffel’s syntax is much clearer, and sinceprogrammers will code small routines, the ‘?=‘ isnot difficult to spot in an Eiffel program. Thereasoning against ‘?=’ in C++ is strange, since Calready provides assignment operators like ‘ += ’ and‘-= ’, which are just a small syntactic convenience.

Another RTTI feature is the typeid operator.

[Stroustrup 94] warns against using this todetermine program flow control based on typeinformation. You should not use switch statements,but use dynamic binding on polymorphic (virtual)functions. This will need to be built into your stylerules that programmers will hate, or you will end uphaving to fix the dirty deed after the fact, whichadds to the expense of your software developments.

Eiffel has no built in operator to achieve this, sothe object-oriented principle of using dynamicbinding instead of switch statements is betterenforced. Eiffel removes type identification from thelanguage, but places it in the libraries in someroutines built into the GENERAL class. So in Eiffel,

it is harder to commit the bad programmingpractices that [Stroustrup 94] warns about.

3. 15 New Type CastsNot only did C++ introduce RTTI anddynamic_cast in March 1993, but also threemore cast operators in November 1993. Theseoperators are:

static_cast<T>(e) ,reinterpret_cast<T>(e) , andconst_cast<T>(e) .

Again for all these the specification of the <type>seems superfluous, as the compiler can derive thatfrom the context. These casts just about cover all thecases where you would need to use C style casts.

[Stroustrup 94] indicates a desire to discard theC casts: “I intended the new-style casts as acomplete replacement for the (T)e notation. Iproposed to deprecate (T)e ; that is, for thecommittee to give users warning that the (T)enotation would most likely not be part of a futurerevision of the C++ standard. ... However, that ideadidn’t gain a majority, so that cleanup of C++ willprobably never happen.”

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 29/63

C++?? 26

3rd Edition © Ian Joyner 1996

The bottom line to these sections on type castscomes again from [Stroustrup 94]: “In all cases, itwould be better if the cast - new or old - could beeliminated.” It can! Use Eiffel or another one of thelanguages in which the type system is more cleanlydefined.

3.16 Java and Casts

Unfortunately, Java needs casts in the aboveexamples, but has improved the situation: “Not allcasts are permitted by the Java language. Some castsresult in an error at compile time. For example, aprimitive value may not be cast to a reference type.Some casts can be proven, at compile time, alwaysto be correct at run time. For example, it is alwayscorrect to convert a value of a class type to the typeof its superclass; such a cast should require nospecial action at run time. Finally, some casts cannotbe proven to be either always correct or alwaysincorrect at compile time. Such casts require a test atrun time. A ClassCastException is thrown if a castis found at run time to be impermissible.” - from the

Java Language Specification.

3.17 ‘.’ and ‘->’The ‘.’ and ‘->’ member access syntax came from Cstructures, and illustrates where the C base adverselyaffects flexibility. Semantically both access amember of an object. They are, however,operationally defined in terms of how they work.The dot (‘.’) syntax accesses a member in an objectdirectly: ‘x.y’ means access the member y in theobject x.

OBJ x; // declare object x of// class obj// with a member y.

x.y; // access y in object x// directly

x->y; // syntax error “. expected”

The specific error is:

error: type 'OBJ' does not have anoverloaded member 'operator ->'

error: left of '->y' must pointto class/struct/union

The ‘->’ syntax means access a member in an objectreferenced by a pointer: ‘ x->y ’ (or the equivalent*(x).y ) means access the member y in the objectpointed to by x .

OBJ *x; // declare a pointer x to an// object of class obj.

x->y; // access y via pointer xx.y; // syntax error “-> expected”

The specific error is:

error:'.OBJ::y' : left operand pointsto 'class', use '->'

In these examples, ‘what’ is to be computed is“access the element y of object x.” In C++,however, the programmer must specify for everyaccess the detail of ‘how’ this is done. That is theaccess mechanism to the member is made visible tothe programmer, which is an implementation detail.Thus the distinction between ‘.’ and ‘ -> ‘compromises implementation hiding, and veryseriously the benefit of encapsulation. We will seein the section on inlines how the visible differenceof access mechanisms between constants, variablesand functions also breaks the implementation hidingprinciple, and how the burden is on the programmerto restore hiding, rather than fix the language.

The compiler could easily restoreimplementation hiding by providing uniform accessand remove this burden from the programmer, as infact most languages do. The major benefit of implementation hiding is that if the implementation

changes, the effect is contained within the classitself; not manifest beyond the interface. Whereimplementation hiding is broken, the effects of implementation change become visible, and thisreduces flexibility.

For example, if the ‘ OBJ x ’ declaration ischanged to ‘ OBJ *x ’, the effect is widespread asall occurrences of ‘ x.y ’ must be changed to ‘ x->y ’. Since the compiler gives a syntax error if thewrong access mechanism is used, this shows that thecompiler already knows what access code isrequired and can generate it automatically. Goodprogramming centralises decisions: the decision toaccess the object directly or via a pointer should be

centralised in the declaration. So again, C++ useslow level operators, rather than the high leveldeclarative approach of letting the compiler hide theimplementation and take care of the detail for us.

Java only supports the dot form of access. The‘-> ‘ form is superfluous. Java objects are onlyaccessed by reference; there are no embeddedobjects.

Eiffel provides a more interesting case. In Eiffelan optimisation is provided as an object can beexpanded in line in another object, in order to save areference. Eiffel calls such objects expandedobjects. There is still no need for explicitdereferencing. The compiler knows exactly whether

the object is expanded or referenced, and thus thedot accessor is used for both, so uniform access isprovided, and the access mechanism is hidden. Thismakes the program more malleable, as theprogrammer can later change an object to expanded,and not have to worry about changing every ‘ -> ‘ toa dot. Conversely, if expansion turns out to beinappropriate, as in the case of a circular reference,then the expanded status of the object can beremoved from the declaration, without having tochange another single line of code. Thus Eiffel

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 30/63

C++?? 27

3rd Edition © Ian Joyner 1996

preserves the implementation hiding principle,which results in convenience for the programmer.

There is even more to Eiffel’s scheme, which isparticularly relevant to concurrent and distributedprocessing. Meyer points out in [Meyer 96c] that theform x.f means passing the message f to the object x.

x may be anywhere on the network. In other words, x might not be a reference that is implemented by an

underlying C pointer, but it may be a network address, for example a URL.

3.18 Anonymous parameters in ClassDefinitionsC++ does not require parameters in functiondeclarations to be named. The type alone can bespecified. For example a function f in a class headercan be declared as f (int, int, char) . Thisgives the client no clue to the purpose of theparameters, without referring to the implementationof the function. Meaningful identifiers are essentialin this situation, because this is the abstractdefinition of a routine; a client of the class androutine must know that the first int represents a‘count of apples’, etc. It is true that well knownroutines might not require a name, for examplesqrt (int) . But this is not appropriate for largescale software development.

The use of anonymous parameters handicaps thepurpose of abstract descriptions of classes andmembers: to facilitate the reusability of software.This is covered in more detail in the section on‘Reusability and Communication’. Program textcaptures the meaning of the system for some futureactivity, such as extension or maintenance. Toachieve reusability, communication of intent of asoftware element is essential.

Names are not strictly necessary inprogramming. Naming exists to help the humanreader identify different entities within the program,and to reason about their function. For this reasonnaming is essential; without it, development of sophisticated systems would be nearly impossible.Some languages access parameters by their address(position) in the parameter list ($1, $2, etc). This isunsatisfactory, even for shell scripts. Anonymousparameters can save typing in a function template,but then programming is not a matter of conve-nience as it is inconvenient for later readers. Theredundancy is beneficial and saves laterprogrammers having to look up the information in

another place. A real convenience in functiontemplates would be that abstract function templatesbe automatically generated from the implementationtext (see header files for more details).

Anonymous parameters illustrate the link between courtesy and safety issues in programming.Due to pressure of work, a client programmer mightwrongly guess the purpose of a parameter from thetype. The failure of the original programmer toprovide a courtesy has caused a client programmerto breach safety. However, the client programmerwill probably be blamed for not taking due care. An

interface client must know the intention of theinterface for it to be used effectively.

Both Java and Eiffel do away with thedistinction between a function definition anddeclaration. The first reason for this is that you don’tneed forward declarations, as entities can bereferenced before they are declared. The secondreason is that in Eiffel, there are tools to

automatically extract abstract interface definitionsfrom the main code.

3.19 Nameless ConstructorsMultiple constructors must have different signatures,similar to overloaded functions. This precludes twoor more constructors having the same signature.Constructors are also not named (apart from thesame name as the class), which makes it difficult totell from the class header the purpose of the differentconstructors. Constructors suffer from all of theproblems described with regards to overloadedfunctions. Firstly, it would be easy to mark routinesas constructors, for example:

constructor make (...)...constructor clone (...)...constructor initialise (...)...

where each constructor leaves the object in valid, butpotentially different states. Named constructorswould aid comprehension as to what the constructoris used for in the same way as function namesdocument the purpose of a function. Secondly,named constructors would allow multipleconstructors with the same signature. Thirdly, it iseasier to match up an object creation with theconstructor actually called. Fourthly, the compilercould check the arguments given in the invocationto the constructor signature.

Java’s constructor scheme is the same as C++.Eiffel allows a series of creation routines. These areindeed independently named as suggested above.

Eiffel has another advantage in that creationroutines can also be exported as normal routineswhich can be called to reinitialize an object. In C++you cannot call a constructor, after the object iscreated.

3.20 Constructors and TemporariesA ‘return <expression>’ can result in a differentvalue than the result of <expression>. In section6.6.3, the C++ ARM says: “If required theexpression is converted, as in an initialisation, to thereturn type of the function in which it appears. Thismay involve the construction and copy of atemporary object (S12.2).”

Section 12.2 explains: “In some circumstances itmay be necessary or convenient for the compiler togenerate a temporary object. Such introduction of temporaries is implementation dependent. When acompiler introduces a temporary object of a classthat has a constructor it must ensure that aconstructor is called for the temporary object.”

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 31/63

C++?? 28

3rd Edition © Ian Joyner 1996

A note says: “The implementation’s use of temporaries can be observed, therefore, through theside effects produced by constructors anddestructors.”

Putting this together, creation of a temporary isimplementation dependent, so might or might not bedone. If a temporary is created, a constructor iscalled as a side effect, which can change the state of

the object. Different C++ implementations couldtherefore return different results for the same code.

3.21 Optional ParametersOptional parameters that assume a default valueaccording to the routines declaration are supposed toprovide a shorthand notation. Shorthand notationsare intended to speed up software development.Such shorthand notations can be convenient in shellscripts, and interactive systems. In large scalesoftware production, however, precision ismandatory, and defaults can lead to ambiguities andmistakes. With optional parameters the programmercould assume the wrong default for a parameter.More importantly, optional parameters underminetype safety. The type of a function is defined by thecomposition of its input types, and its output type:

f : T1 x T2 x T3... -> T4

The entire signature determines the type of thefunction, not just the return type. Optionalparameters mean that C++ is not type safe, and thatthe compiler cannot check that the parameters in thecall exactly match the function signature.

Furthermore, they do not provide a great deal of convenience. If a routine has five parameters, thelast three of which are optional, and the caller wantsto assume the defaults for parameters 3 and 4, butmust specify parameter 5, then all five parametersmust be specified. A better scheme would be to havea ‘default’ keyword in function calls:

f (a, b, default, default, e);

Other means, already in the language, can easilyprovide this mechanism. For example, a call toanother (possibly inline) function could provide thedefaults for the optional parameters:

g(a, b, e); // the callg(int a, b, e) // the function

{f(a, b, 0, 0, e);}

This not only provides the convenience of optionalparameters, but is more powerful. Any parameter orcombination can be filled in with any combinationof defaults, not just the last parameters. Multipleintermediate routines can provide multiple sets of defaults.

Neither Java nor Eiffel have optionalparameters. Strong typing is enforced, so that theparameters of a call must match the routinesignature.

3.22 Bad DeletionsThe following example is given on p.63 in the C++ARM as a warning about bad deletions that cannotbe caught at compile-time, and probably notimmediately at run-time:

p = new int[10];p++;

delete p; // errorp = 0;delete p; // ok

One of the restrictions of the design of C++ is that itmust remain compatible with C. This results inexamples like the above, that are ill-definedlanguage constructs, that can only be covered bywarnings of potential disaster. Removal of suchlanguage deficiencies would result in loss of compatibility with C. This might be a good thing if problems such as the above disappear. But then theresultant language might be so far removed from Cthat C might be best abandoned altogether.

Bad deletions are the kind of problem the Javadesigners set out to avoid. You do not get baddeletions in either Java or Eiffel for two reasons:firstly, they do not have pointers; secondly, theyprovide garbage collection so don’t delete objects.

3.23 Local entity declarationsDeclaring an entity close to where it is used, hasadvantages and disadvantages as it is convenient,but can make a routine appear more complex andcluttered. A problem is that an identifier can bemistakenly overloaded within a nested block in afunction, with the resultant problems covered in thesection on name overloading. C does not have

nested routines or blocks so does not have thisproblem. ALGOL uses this simple form of nameoverloading. (A block in the ALGOL sense containsboth declarations and instructions.)

The ARM explains problems of localdeclarations with branching, which shows thecomplications in intermingling declarations andinstructions. Caveats cannot make up for or fix afaulty language definition.

In well written object-oriented software, routineswill be small, typically performing one atomicoperation per routine, so localised declarations willnot be of much value. Small routines that implementatomic operations are fundamental to loose

coupling. For example, a base class that provides asingle routine that logically performs operations Aand B, is not useful to a subclass that needs toprovide its own implementation of B, but does notwant to change A: the descendant must reimplementthe logic of both A and B, missing an opportunity toreuse the logic of A. Splitting A and B into differentroutines accomplishes loose coupling, and thereforeflexibility. Tight coupling reduces flexibility.

Efficiency is also attained without the mess of local entity declarations. Good design and cleanmodularisation achieve efficiency, as the entities

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 32/63

C++?? 29

3rd Edition © Ian Joyner 1996

which would be locals to a block in C++ are onlycreated when the routine is entered. Furthermoresmall routines can be inlined, and in this case, thelocals will only be created when the expanded inlineblock is entered, which is the same effect as if theprogrammer had included the block manually.

Java implements locals in the same way as C++.In Eiffel the philosophy is to use good design to

make routines sufficiently small and atomic. That isone operation, one routine. With this approach,having local declarations only in one place in theroutine and not throughout is sufficient. If you find aplace where you want to introduce local variableswithin the code, this is an indication that you shouldwrite it as a separate routine. An objection could bethat small routines with lots of overhead callingthem is not efficient. Eiffel compilers solve this byautomatically inlining routines. Thus the integrity of a design is preserved in the program text, butefficiency is retained. In C++ you could manuallyinline such functions.

3.24 MembersCare should be taken with the C++ use of the termmember. In general use, an object is a member of aclass. For example, squirrels are a member of theclass animal. This corresponds to members in settheory. But in C++, the term member means a dataitem, or function of the class. Some people mightsay that set theory is one thing, but programming isanother, so there is no problem with using theterminology. However, set theory underpins thetheory of computation and programming, and sets,classes and types are related. Sets are a means of describing groups of entities which have somesimilarity. Supersets group entities according to

broad concepts; subsets group entities according tonarrower concepts, that is, more restrictive criteria.So sets also underpin our understanding of classesand subclasses.

In set theory we say: 3 N, or 3 is a member of the set of natural numbers. In objects we would saythat Fred is a member of the class person. In C++the field name which for some object contains thestring “Fred” is a member of the class person. Thisis not mathematically correct, and the confusioncould have been avoided.

Java does not seem to use the term member. Itmight stick from C++. Eiffel uses the term features .

3.25 InlinesThe problems described in this section are aconsequence of placing the burden of encapsulationon the programmer. You might wish to review thesection on encapsulation at this point.

The main reason inlines were introduced in C++was to alleviate the cost of crossing the ‘protectionbarrier’, [Stroustrup 94]. The protection barrier inC++ is data hiding. When accessing a data item inC++, it is recommended not to do it directly, but viaa class member function. For example, given an

object reference c you should not access the datamember di directly:

i = c.di; // Not recommended C++ style.

instead di should be private and accessed asfollows:

i = c.get_di();

where get_di is:int C::get_di() {return di;}

However, Stroustrup found that some programmerswere not using an access function because of theoverhead of a function call. So inlines wereintroduced:

inline int C::get_di() {return di;}

Note that this style of data hiding clutters the namespace and text of a class.

The inline mechanism has two conceptualmistakes and a practical one. Firstly, data hiding andimplementation hiding are not the same.Implementation hiding is more to do with hiding themechanics of the access mechanism, so that youcan’t tell whether it is a constant, variable of function you are accessing. Inlines are the wrongsolution to this problem: the correct solution isuniform access. The OO concept is to hideimplementation; data need not be private, but maybe functionally exported from the classes interface.

This leads to the second conceptual mistake thatfunctional access and C functions are differentthings. Functional access hides the accessmechanism. C functions, however, make the accessmechanism visible: you know you are invoking apiece of code that will be jumped to. Functionalaccess by contrast is any entity name that can occurin the context of an expression. This entity could bea constant, variable or value returning routine, butyou can’t tell which if the implementation of theaccess mechanism is hidden. The statement i =c.di is functional access. C++ has solved thisproblem in exactly the wrong way in order to staycompatible with the flawed concept of function in C.

The programmer is required to bear this burden,which in turn makes software development morecostly for every company using C++, and againflexibility is reduced. In order to restore informationhiding, that is access transparency betweenconstants, variables and C functions, programmersmust as a matter of style hide constants andvariables behind a C function, as is the case withget_di() . A fix to the language would have beenbetter, but not possible to keep compatibility with C.

The practical mistake is that a compiler canautomatically generate inlines. Requiring aprogrammer to specify inline is a manualbookkeeping task. It is not hard for a compiler oroptimiser to work out that C::get_di(){return di;} or even more complex routinescould be inlined. This is exactly the kind of

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 33/63

C++?? 30

3rd Edition © Ian Joyner 1996

optimisation that Eiffel and other sophisticatedlanguages perform.

[Flan 96] says: “A good Java compiler shouldautomatically be able to “inline” short Java methodswhere appropriate.” An article in Byte of September1996 suggests that to optimise Java method calls,“you should make liberal use of the finalkeyword.” Byte also suggests that instead of small

functions, programmers should inline by hand smallmethods. Byte further says: “The trade-off, then, iseither better performance or code flexibility. Youmust decide which is most important to theprogram’s operation in that situation.”

In this respect, Eiffel again proves itself superior. Eiffel automatically determines that aroutine is final , or in C++’s terminology, that aroutine is not virtual . Also Eiffel automaticallyinlines. Therefore the Eiffel programmer does notneed to bend the code to gain performance, orconsider trade-offs: you do not have to trade-off flexibility to gain performance.

Eiffel has a further advantage that it understands

the difference between implementation hiding anddata hiding and provides implementation hiding. Italso accesses data and constants functionally, so inthe instruction:

i := c.di

you can’t tell and don’t need to know whether di isimplemented as a constant, variable or routinefunction. The implementation is hidden: access isuniform as access to a constant or variable looks thesame as a value returning routine, and the differentaccess mechanisms behind these is hidden andautomatically generated by the compiler. And sincethis implementation distinction is hidden, the need

is greatly reduced for either the programmer tomanually inline, or for the compiler to automaticallyinline. In this case Eiffel provides the maximumflexibility.

Since C functions are poor cousins tomathematical functions, and C++ also confuses datahiding and implementation hiding, the languageincludes otherwise unnecessary mechanisms likeinline.

3.26 FriendsFriends are a mechanism to override data hiding.Friends of a class have access to its private data.Friend is a ‘limited export’ mechanism. Friendshave three problems:

1) They can change the internal state of objectsfrom outside the definition of the class.

2) They introduce extra coupling betweencomponents, and therefore should be usedsparingly.

3) They have access to everything, rather thanbeing restricted to the members of interest tothem.

Friends are useful, and a case can be made forshades of grey between public, protected and private

members. An alternative to friends is multipleinterfaces which provide the functionality of friendsand avoid the above problems. Each interface to aclass can be exported to everything, or to selectedclasses only. A selective export mechanism is moregeneral than public, private, protected and friend,and explicitly documents the couplings betweenentities in the system. Selective export specifies not

only that a member is exported but to which classesit is exported.One reason given for friends is they allow more

efficient access to data members than a memberfunction call. The way C++ is often used is that datamembers are not put in the public section, becausethis breaks the data hiding principle.

As mentioned in the section on inlines,implementation hiding is different to data hiding. Aslong as you access your data functionally, you donot have to hide your data, just the accessmechanism.

Another question is, since there are inlines, isthere a need for the similar mechanism of friends? If

you mark a function inline, it is going to expandinline, and avoid the function call overhead. So inthis case, friend is a superfluous mechanism.

In Java, classes in the same package can accessinstance variables from other classes in a friendlyfashion. This is contrary to good programmingpractice and OO design, as it means you can accessthings without going through the published interfaceof a class. However, in Java, explicit friends aregone.

Eiffel offers the pure OO approach, whereeverything must go through publicised interfaces.Note in Eiffel that data attributes in a class may beexported in the published interface, as access is

uniform. In that case, external entities can read thedata, as if it were invoking a function, but youcannot write to a data item in an external class. Toupdate a data item, you must call an updateprocedure. Part of the purpose of friend is to updatean item directly, without the overhead of aprocedure call. In Eiffel the compiler willautomatically inline procedures where possible, sothe efficiency concern is addressed.

To summarise: Eiffel does not need the friendmechanism for two reasons: firstly, external classescan access data attributes for reading; secondly, forupdate, a procedure is expanded inline wherepractical. Accessing a data item does not contravene

encapsulation or implementation hiding. Data hidingis not encapsulation, although with encapsulationimplementation data is hidden, the operative wordbeing ‘implementation’, not ‘data’.

3.27 Controlled exports vs friendsAs noted in the section on friends, there is a case forfiner grained control of exports than public ,private and protected . Except for friends,Java uses the same mechanism as C++, but adds twomore categories, default and private

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 34/63

C++?? 31

3rd Edition © Ian Joyner 1996

protected . This complicates the mechanism, andit is difficult to remember exactly what eachcategory does. Eiffel does not have friends, it allowsclasses to be related by a finer grained exportmechanism; for any set of features, you can specifyexactly what classes they are exported to. Classesthat are closely related export to each otherinterfaces that are not available to other classes

outside of that group.Also in Eiffel, you can export a routine to adifferent set of classes based on whether it is calledas a creation routine (constructor), or normal routinecall.

In Eiffel all features are implicitly public .Public can also be explicitly stated by exporting toclass ANY , ie., the universal set. If a set of featuresis to be protected , ie., internal and not visible toclients, it is exported to class NONE . Such a set of features is secret . NONE is the equivalent of theempty set in set theory, which is notionally a subsetof all sets and NONE is a subclass of all otherclasses, and has only one possible value: Void .

There is no equivalent of private in Eiffel,where features can be hidden from sub-classes. Butthis is not necessary, and in most cases private isundesirable. The Eiffel philosophy is that withinheritance you get unrestricted access to theimplementation as this is key to the flexibility of reuse and extension. As a subclass, you can redefineany routine inherited from a parent. When youredefine a routine, you are changing theimplementation. Since you are changing theimplementation, the private restriction could be anuisance to some subclass that hasn’t been writtenyet. If you need to access a variable, and the parentclass designer has made it private , you are out of

luck. At the best you could go to the programmerwho owns that class, and try to convince them tomake the variable protected . Good luck: thatkind of request often generates a lot of heat. At theworst you can do nothing about it because the classmight be from outside and closed to you. Again inC++ the parent class designer is forced to makedecisions that should be left open. I wouldrecommend against using private , useprotected instead. At least protected leavesthe class open under inheritance.

In C++, private only restricts access, it doesnot restrict visibility in a subclass. With private ,it is still possible to redefine a privatevirtual

function from a base class in a subclass.This is not a problem, but you cannot preventredefinition in a subclass, as you can with the Eiffelfrozen mechanism.

In Java you cannot override a private method,but you can overload it: “Note that a private methodis never accessible to subclasses and so cannot behidden or overridden in the technical sense of thoseterms. This means that a subclass can declare amethod with the same signature as a private methodin one of its superclasses, and there is norequirement that the return type or throws clause of

such a method bear any relationship to those of theprivate method in the superclass.” [Sun 96].

A further complication in C++ is that public ,private , protected can be specified wheninheriting a base class. This gives one policy forhow every inherited member from the base class isto be treated in the new class. A problem with this isthat once a member is private or protected, it cannot

be reexported, ie., protected cannot be madepublic , and private cannot be madeprotected or public . Thus the temptation for aC++ programmer is to keep things public, as aderived class might want something to be public,even though it does not make sense to be public inthe base class. Again decisions must be made earlyon issues you don’t know about.

Java has no equivalent. Each member isinherited with the same public , private ,protected attribute as the base class.

Eiffel again has a more fine grained approach.The export policy for each feature inherited from aparent class can be reviewed on a case by case basis.

The export status of each feature can be changed andmade more or less restrictive. If there is no newexport policy, the default is the same as the parentclass. The designer of a parent class does not have toconsider what descendant classes need, or worryabout the case where their needs will be in conflictwith each other, as the designer of the descendantclass has complete flexibility, which enhances reuseand extensibility. Eiffel’s export mechanism istherefore vastly superior to the C++ approach.

3.28 StaticThe word ‘static’ is confusing in C++. Page 98 of the C++ Annotated Reference Manual (ARM)mentions this confusion and gives two meanings: aclass can have static members, and a function canhave static entities; and the second meaning comesfrom C, where a static entity is local in scope to thecurrent file. The choice of different keywords wouldeasily solve this confusing use of the same keywordfor several meanings. There is also a third moregeneral meaning that objects are statically orautomatically allocated and deallocated on the stack when a block is entered and exited, as opposed todynamically allocated in free space. Another generaluse of the word ‘static’ is in ‘static type checking’,which obviously has no relation to the C uses, butoverloads the language even further.

Static class members are useful. Page 181 of theARM states that statics reduce the need for globalvariables, which is good thing, but the C syntaxobscures the purpose.

Locals declared in functions can also be static.These are not needed in an object-oriented language.The reason and history is this: ALGOL has thenotion of ‘OWN’ locals in blocks. The semantics of an OWN entity is that when a block is exited, thevalue of the OWN is preserved for the next entry tothe block, ie., the value is persistent. The

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 35/63

C++?? 32

3rd Edition © Ian Joyner 1996

implementation is that at compile time, the OWNentity is limited in scope to the block, but at runtime, it is located in the global stack frame. Thesame instance of the variable is used in allinvocations of the procedure, rather than eachinvocation using separate local storage on the stack.This causes complication in recursion.

Simula’s designers generalised the ALGOL

notion of block into class, and so object-orientationwas born. Instead of discarding a class block on exit,it is made ‘persistent’. Declarations within the classblock are persistent, and therefore provide thefunctionality of static and OWN, which wasremoved from Simula. Classes are more flexiblethan statics. Statics are persistent in the same way asglobals, ie., for the duration of the program. Classmember lifetime is governed by the lifetime of theobject so object-oriented languages do not needglobals, OWNs or statics.

Java implements class variables with static.Eiffel uses once routines in order to do away withglobals.

3.29 UnionUnion is another construct that is superfluous inOOP. Similar constructs in other languages arerecognised as problematic: for example,FORTRAN’s equivalences, COBOL’sREDEFINES, and Pascal’s variant records. Whenused to overload memory space these force theprogrammer to think about memory allocation.Recursive languages use a stack mechanism thatmakes overloading memory space unnecessary, as itis allocated and deallocated automatically for localswhen procedures are entered and exited. Thecompiler and run time system automatically allocate

and deallocate storage as required, ensuring that twopieces of data never clash for the same memoryspace at one time. This is essential so that theprogrammer can concentrate on the problemdomain, rather than machine oriented details. Whenunion is used similarly to FORTRAN’sequivalences it is not needed.

Union is also not needed to provide theequivalent to COBOL REDEFINES or Pascal’svariants. Inheritance and polymorphism provide thisin OOP. A reference to a superclass can also be usedto refer to any subclass, and thus provides the samesemantics as union, only in a type safe manner, asthe alternatives can never be confused. An object

reference is implicitly a union of all subclasses.Union can also be used to suppress typechecking. [Stroustrup 94] says “programmers shouldknow that unions and unchecked function argumentsare inherently dangerous, should be avoidedwhenever possible, and should be handled withspecial care when actually needed.”

Sun recognises that the union construct isunnecessary, and has removed it from Java. Noequivalent exists in Eiffel.

3.30 StructsStruct is only in C++ as a compatibility mechanismto C. When you have classes you don’t need structs.Again, C++ is unnecessarily complicated withunneeded features.

[Sun 95] says: “The Java language has nostructures or unions as complex data types. Youdon’t need structures and unions when you have

classes - you can achieve the same effect simply byusing instance variables of a class.”Eiffel and Smalltalk similarly have no

equivalents to struct.

3.31 TypedefsTypedef is yet another mechanism not needed. Java,Eiffel and Smalltalk all build their type mechanismsaround classes.

3.32 NamespacesNamespaces are a new concept introduced in July1993. Namespaces address the problem that globalnames imported from different .h header files canclash. The C++ solution is namespaces, whereglobals are put in a namespace. Access to theseentities must be qualified with the namespace name.For example, A::x means access entity x innamespace A. Another namespace B might also havean entity named x , but these names will not clash.Entities not in a namespace are considered to be inthe global namespace.

In pure OO languages, namespaces are notneeded; classes themselves are namespaces. Thereare no global environments, so C++ introducescomplexities not needed in Java, Eiffel andSmalltalk.

Java and Smalltalk have class variables, whichcan be used in place of globals. Eiffel provides onceroutines, so that you can access object instanceswhere your ‘globals’ are stored.

Namespaces address the problem of nameclashing entities. However, the names of thenamespaces themselves can clash. For example, if two header files have namespaces called MY_NS,you have a clash.

As you might be aware by now, name clashesare a nuisance whenever you mix and matchsoftware entities together. An example we have seenis multiple inheritance. Eiffel provides a goodsolution to this with the rename clause in theinheritance clause.

Eiffel could also have a problem with classname clashes, as class names are global. Thesolution to this is to use a deployment languageseparate from Eiffel itself. This language is calledLACE, Language for the Assembly of Classes inEiffel. The concern of LACE is to mix and matchclass libraries together, and it provides mechanismsto rename classes, and resolve other conflicts. Thatway, deployment concerns are kept separate fromthe programming concerns.

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 36/63

C++?? 33

3rd Edition © Ian Joyner 1996

While namespaces in C++ address a problem,they rely on programmers to be courteous, and placeglobals in namespaces. Perhaps a better way, wouldbe to have a separate mechanism equivalent toEiffel’s LACE where such conflicts are resolved,rather than making the language even morecomplex.

3.33 Header FilesIn C++ a class interface must be maintainedseparately from its body. An abstract class interfaceis just the class with the implementation detailremoved so the interface and implementation canboth be maintained in one source. In C++ though,programmers must maintain the two sets of information. This is because of the C/Unix style of programming with separate modules but little or noglobal analysis. Replicated information has the wellknown drawback that in the event of change, bothcopies must be updated. Sun calls this “The FragileSuperclass Problem.” [Sun 95] This can lead toinconsistencies that must be detected and corrected.

Classes that depend on another class must berecompiled if the layout of that class changes. Toolscan automatically extract abstract class descriptionsfrom class implementations, and guaranteeconsistency.

Splitting C and C++ programs into a myriad of small, separately compiled files turns out not to be agood way to organise projects, and not a good wayto program, as you must maintain many header files.Some people are now finding it more convenient tokeep an entire large system in one file as it solvesmany maintenance problems, and also makes iteasier to find things during editing. Unfortunately,while this scheme on many systems allows for

global analysis, this will still not solve the problemsarising from lack of global analysis in C++.The programmer must also use #include to

manually import class headers. #include is anold and unsophisticated mechanism to providemodularity. #include is a weak form of inheritance and import. C++ still uses this 30 yearold technique for modularisation, while otherlanguages have adopted more sophisticatedapproaches, for example, Pascal with Units, Modulawith modules, Ada with packages. In Eiffel the unitof modularisation is the class itself, and includes arehandled automatically. The OOP class is a moresophisticated way to modularise programs.

Inheritance implements reusability andmodularisation, so #include is superfluous.Another problem is that if header A includes

header B, and header B includes header A, a circulardependency occurs. The same problem occurs if header A includes headers B and C, and header Balso includes header C. A simple but messy fix in allheaders solves this problem:

#ifndef thismod#define thismod

... rest of header#endif

Headers show how C++ addresses the problem of independent modules with a non-object-orientedapproach that is sub-optimal; the programmer mustsupply this bookkeeping information manually.#include relates to the organisation andadministration of a project. Rational language de-sign eliminates such manual bookkeepingmechanisms.

A class interface is equivalent to a moduleheader. A module header contains data and routinesexported to other modules. This is exactly thepurpose of the class interface. Furthermore, in C++a tool like make must be used to specify thedependencies.

A class definition contains all knowledge of accessed classes and their dependencies (inheritanceand client) in the class text. Dependency analysis isderivable from the class text, and much of thefunctionality of tools like make can be integratedinto the compiler, so the errors and tediumencountered in the use of make are avoided.Dependency analysis also implements a level of dead code elimination .

A traditional system is assembled by combiningmodules; an object-oriented system is assembled bycombining classes. Modules are a primitive form of classes; classes are more sophisticated. They expressmore precisely relationships with other classes. C++#include and modules have problems. Thisprimitive method is not required in an object-oriented language.

According to Stroustrup C++ would be a betterlanguage without the C preprocessor. Most uses of #define are now covered by other mechanisms.To remove #include would require some otherimport mechanism. [Stroustrup 94] says: “I’d like tosee Cpp abolished.”

Neither Java, nor Eiffel need header files or the#include mechanism. This means thatprogrammers do not have to maintain headersseparately. When Eiffel sees any declaration:

c: C it knows the current class has a dependency on theclass C . C is implicitly imported, so there is no#include mechanism: Eiffel has done thedependency analysis for you. If you add a newdeclaration to a class that hasn’t be used before, thedependency is automatically generated the next timethe class is compiled.

Java maps qualified class names such as java.lang.Math to the environments file directorystructure, for example java/lang/Math in Unix.

Eiffel provides a utility short that extracts classinterface definitions from the class implementation.However, the function of this is for humanreadability, not to provide the compiler with classdefinitions as in a C header file.

Eiffel also separates the bookkeeping concernsfrom the language. These functions are provided by

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 37/63

C++?? 34

3rd Edition © Ian Joyner 1996

the LACE language, Language for the Assembly of Classes in Eiffel . LACE is used separately to theEiffel language, but is processed by the compiler tomap class names to their location (directory and filename in Unix style systems.)

Java and Eiffel also remove the need for make .Gone is the manual dependency analysis, orremembering to rerun makemake , when your

dependencies change.3.34 Class InterfacesSection 9.1c of the C++ ARM points out that C++has no direct support for “interface definition” and“implementation module”. In a C++ class definition,all private and protected members must be includedin the public text of the class. The ARM points outthat whenever the private or protected parts arechanged, the whole program must be recompiled.Further to what the ARM says, all modules that aredependent on the header file must be recompiled,even though the private and protected members donot affect other modules. Private members shouldnot be in the abstract class interface, as this exposesimplementation details to programmers of clientmodules.

3.35 Class Header DeclarationsC’s syntax for function declarations is [<type>]<identifier> (<parameters>). For (a very simple)example:

class C{

a ();b ();int c ();d ();char e ();virtual void f ();

}

To find an identifier in this layout, the eye musttrace a course around the type specifications andmodifiers, which is a tiring activity. There is agreater chance of missing the sought identifier, andthe programmer must resort to using the searchfunction of a text editor to help out.

Other languages place the entity names first. Forexample:

class C{a ();b ();c () int;d ();e () char;f () virtual void;

}

To those used to the ALGOL and FORTRAN styleof type first, this seems backwards. But name first is

logical as a real world example illustrates: imagineif a dictionary was published where the keywordswere not placed first, but rather the entry order is -

noun /obvrzen/ obversion, the act orresult of obverting

Such a dictionary would not sell many copies, unlessthe marketeers managed to fool many people thatthe explanation of the meaning was better becausethe order of layout was mysteriously magical. Thisexample illustrates how important subtle syntaxdecisions are, and why Pascal style languages haveordered things contrary to FORTRAN, ALGOL andothers. The language designer must consider thesetrivial but important alternatives. The layout of programming entities is essential for effectivecommunication. The dual roles of language syntax,and programming style affect comprehension. Adictionary or index style layout suggests placingentity names first, followed by their definition.

Java obviously has to retain this problem since itis C based. In fact the hello world program in Java

shows how putting an entity name after modifierscan obscure the program:

public static void main(...)

Eiffel mostly puts the feature name first, except forthe frozen case, so that features are easier to find.The frozen modifier is not used very often though.

3.36 Garbage CollectionOne of the hallmarks of high level languages is thatprogrammers declare data without regard to how thedata is allocated in memory. In block structuredlanguages, local variables are automaticallyallocated on the stack, and automatically deallocatedwhen the block exits. This relieves the programmerof the burden of allocating and deallocatingmemory. Garbage collection provides equivalentrelief in languages with dynamic entity allocation.

In C++ the programmer must manually managestorage due to the lack of garbage collection. This isthe most difficult bookkeeping task C++programmers face that leads to two oppositeproblems: firstly, an object can be deallocatedprematurely, while valid references still exist(dangling pointers); secondly, dead objects mightnot be deallocated leading to memory filling up withdead objects (memory leaks). Attempts to correcteither problem can lead to overcompensation and theopposite problem occurring. A correct system is afine balance. This is illustrated in the figure below.

Dangling Correct MemoryPointers System Leaks

These problems contribute to the fragility of C++programs, and usually result in system failure.Garbage-collection solves both problems, but has anundeserved bad reputation due to some earlygarbage-collectors having performance problems,

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 38/63

C++?? 35

3rd Edition © Ian Joyner 1996

instead of working transparently in the background,as they can and should. These problems are oftenover-emphasised as a justification for C++ ignoringgarbage collection. A possible solution is to buildgarbage collection into the run-time architecture, butallow the programmer to activate and deactivate itmanually. Garbage collection can be disabled insystems where it is inappropriate.

In C++ it might be argued that the lack of garbage-collection is not an engineeringcompromise. Its inclusion is nearly an engineeringimpossibility, as a programmer can undermine thestructures required for implementing correctlyworking garbage-collection. While garbage-collection might not actually be an impossibility inC++ (EC++), it is difficult, and programmers wouldhave to settle for a more restricted way of programming. This could be a good thing. But thenthe compromise to remain compatible with Cbecomes difficult, if the compiler is to detectpractices inconsistent with the operation of garbage-collection.

[Sun 95] states that “explicit memorymanagement has proved to be a fruitful source of bugs, crashes, memory leaks and poorperformance.” Sun have built garbage collection intoJava.

Bertrand Meyer lists garbage collection in hissteps to object-oriented happiness. This is notsurprising in a language that has exception handling,keeping track of live and dead objects is even moredifficult, so Eiffel is also based on built-in garbagecollection.

Stroustrup is also an advocate of optionalgarbage collection. In [Stroustrup 94] he states“When (not if) garbage collection becomes

available, we will have two ways of writing C++programs.” My question is not if or when, but how?Unless you restrict pointers and pointer operations,garbage collection will be very difficult, andprobably inefficient. By inefficient, I mean eitherslow, or it won’t clean up very well, or even both.

In Eiffel garbage collection is also optional. Thegarbage collector can be disabled during critical realtime phases of program execution. It cannot becompletely disabled, as if a program runs out of memory in this state, the garbage collector will beinvoked, which is always preferable to theapplication crashing irrecoverably.

3.37 Low level codingOne of the stated advantages of C++ is that you canget free and easy access to machine level details.This comes with a down side: if you make a greatdeal of use of low level coding your programs willnot be economically portable.

Java has removed all of this from C, and one of Java’s great strengths is its portability betweensystems, even without recompilation.

The Eiffel solution is somewhat different again.In Eiffel you have no access to machine and

environment level details, in the language itself.You can use libraries that provide access to routineswritten in external languages like C. You can stillwrite your low level C routines, and easily accessthis level from Eiffel. The major advantage of thisapproach is that all system level code is centralisedin a few places, and this provides good separation of concerns . If you have to port your system, you

know exactly which parts of code will needattention. System interfaces are thus provided in aset of well designed classes and routines. In C++you can only enforce this as a matter of disciplineover your programmers.

3.38 Signature VarianceWhen redefining a routine, there is an opportunity toredefine the signature as well. There are three waysa language can do this known as: no variance,contra-variance, and co-variance. This is an issue of type safety.

No variance means that the language does notpermit the signature to change. The signature mustexactly match the signature inherited from theparent.

Contra-variance means that the signature in asubclass can modify each argument so that it is asuperclass of the matching parent argument. Forexample, if you have classes A and B, and B inheritsfrom A, then given a parameter of type B in yourparent, you can keep it as B or modify it to A. Thisdoes seem counter intuitive, but there are some goodexamples of where it works.

Co-variance is the opposite of contra-variance.In the above example, if your parent has a parameterof type A, you can keep it as A, or redefine it to anydescendant of A. This is more intuitive than contra-variance. In either scheme, a compiler can check fortype-safety.

C++ and Java offer no variance for polymorphicmethods. The reason for this is that if you have aroutine with a different signature, even if theparameters of the parent and child are typeconformant, the method overloads rather thanoverrides the original method. Overloading can be amajor cause of confusion and errors. Many otherlanguages require that a redefined routine must beexplicitly marked as redefined or overridden.

As stated before a simple solution to theoverloading problem would be to require thatprogrammers mark the methods: override oroverload . The compiler could then check forconsistency, that the parameters for an overridingmethod are an exact, or co/contra-variant match, andthat for an overloaded method, the parameters aredifferent. Making overriding and overloadingexplicit is also good documentation, as it is a doublecheck of what the original programmer reallyintended. Remember that overriding choosesbetween the alternative methods at run-time, basedon the type of the owning object; overloading

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 39/63

C++?? 36

3rd Edition © Ian Joyner 1996

chooses between the alternative methods at compiletime based on the argument types.

Eiffel is an interesting case. Contrary to manystrong opinions and theoretical arguments in supportof contra-variance, Eiffel chooses the intuitive co-variant approach, claiming this is useful in manymore situations. Eiffel has also implemented co-variance in such a way that it is type safe.

3.39 Pure Virtual FunctionsPure virtual functions provide a means of leaving afunction undefined and abstract. While the conceptis correct, this section shows both the syntax, andthe terminology ‘pure virtual’ leave something to bedesired. A class that has such an abstract functioncannot be directly instantiated. A non-abstractdescendant class must define the function. The C++pure virtual syntax is:

virtual void fn () = 0;

This leaves the reader new to C++ to guess itsmeaning, even those well versed in object-orientedconcepts. ‘=0’ might make sense for the compilerwriter, as the implementation is to put a zero entryin the virtual table. This shows how implementationdetails which should not concern the programmerare visible in C++.

A better choice would have been a keyword suchas ‘abstract’. Abstract should have syntacticsignificance as abstract functions are an importantconcept in object-oriented design. The C++ decisionin keeping with the C philosophy of avoidingkeywords is at the expense of clarity. A keywordwould implement this concept more clearly. Forexample:

pure virtual void fn ();or

abstract void fn ();

The mathematical notation used in C++ suggeststhat values other than zero could be used. What if the function is equated (or is that assigned) to 13?

virtual void fn () = 13;

A function is either implemented or undefined. Thisto any analyst suggests a boolean state, which asingle keyword conveys. A simple suggestion to fixthis is to define ‘= 0’ as abstract:

#define abstract = 0

then

virtual void fn () abstract;

Let’s look at =0 a slightly different way, as a keyphrase, or a keyword which is spelt with thecharacters ‘ =0’ . If you do that, then the objection tokeywords becomes a non-issue.

As for the terminology, ‘pure virtual’ is acontortion of natural language. It combines words

that are somewhat opposite in meaning. Pure meanssomething that really is what it appears to be, as in

pure gold . Virtual means something that appears tobe what it actually is not, as in virtual memory .Perhaps pure virtual gold is fools gold. Ashas been said before, virtual is a difficult concept tograsp. When it is combined with a word such as‘pure’, the meaning becomes more obscure.

[Stroustrup 94] gives the curious tale about the‘curious =0 ’ syntax: “The curious =0 syntax waschosen over the obvious alternative of introducing akeyword pure or abstract because at the time Isaw no chance of getting a new keyword accepted.Had I suggested pure , Release 2.0 would haveshipped without abstract classes. Rather than riskingdelay and incurring the certain fights over pure , Iused the traditional C and C++ convention of using0 to represent “not there.””

Mathematically, 0 does not normally represent“not there”. Usually, 0 is just another number. Using0 to represent “not there” leads to semanticproblems which lead to many interesting discussions

on topics such as 3 value and 4 value logic, etc. Inthe C world, there are constant arguments overwhether NULL is 0 or something else. In thedatabase world, a value is needed for “not known.”If 0 is used for “not known,” then there is a problemif the value is known, but happens to be 0. The =0syntax is an aggregation of errors. Not only arekeywords such as virtual and staticoverloaded, but worse a number such as zero tomean things that it does not mathematicallyrepresent.

Java and Eiffel use much clearer syntax. Javasimply uses:

abstract void fn ();

In Eiffel you specify the routine as deferred ,meaning the details of implementation are deferredto a descendant class:

r is deferred end

The ‘end’ might look like syntactic baggage, butyou can specify other abstract properties of adeferred routine in the form of pre and postconditions.

Eiffel uses the best terminology, as deferredmeans the implementation is deferred. A routine thathas an implementation still has an abstract form.The abstract definition of the routine is obtained bythe short tool, which extracts the routine signature,that is name, parameters, type, and pre and postconditions from the other details. The term abstractdoes not necessarily mean ‘not implemented’.

3.40 Programming by ContractA common problem programmers face is thatimplementation hiding is very nice in theory, butoften, you actually have to look at the internals of aclass and its routines to determine what the classdoes and how to use it. Often you must examine the

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 40/63

C++?? 37

3rd Edition © Ian Joyner 1996

internals of a routine before you call the routine sothat it works correctly, and to determine its exacteffect after the routine has executed. The signaturespecification of a routine is not enough; routinesoften have side effects.

Eiffel extends the concept of routine signature:what you must set up prior to calling a routine isdocumented as preconditions in the requires clause,

and the exact effect of a routine is documented aspostconditions in the ensures clause. The short tool,extracts the preconditions and postconditions withthe abstract part of a routine signature, asdocumentation for clients of a class. Preconditionsdocument the obligations of the caller and benefitsto the called routine, and postconditions documentthe obligations of the called routine and benefits tothe caller: hence the term programming by contract.

Programming by contract is a major technique insaving programmers from having to look atimplementation code, and is most important tolibrary vendors who don’t want to give away theinternals of their implementation, but do want

people to buy and use their library.Programming by contract is not just a fancydocumentation scheme, but the preconditions andpostconditions provide run time checks to ensurethat all units of the program are behaving correctly,and thus fulfilling their contracts. This is themechanism that detects the run-time inconsistenciesdiscussed in the section on correctness. In Eiffel,this mechanism is integrated with the exceptionhandling mechanism. In C++ and Java you can useassertions for run time checks, but these are notintegrated into the programmers mindset as inEiffel.

Programming by contract is the equivalent to

integrated circuit specifications in the electroniccomponent world, and also tolerances in morephysical engineering disciplines. In Eiffel, thecombination of static type checking withpreconditions and postconditions, integrated withexception handling form a significant way to testthat the software jig-saw puzzle fits together, andthat the resulting picture makes sense. Thesetechniques significantly reduce dependence on ‘afterthe fact’ manual testing.

Neither Java nor C++ have this mechanism.Another interesting case is CORBA IDL, whichbeing an interface language for distributed objects,contract information is important. It is a glaring

omission from CORBA IDL which has glaringinclusions of struct, typedef, union, etc., all of whicharen’t helpful in a distributed object environment,where the concept of programming by contract iseven more important in considering how to connectall the system components together, and you wantmore confidence that the distributed jig-saw fitstogether. In fact this biases CORBA to Cimplementations. The industry should stop andthink, design things carefully and correctly, and stopdesigning things to look like C. So often C

constructs are inappropriate, and make adoptingmore advanced and necessary concepts difficult.

3.41 C++ and the software lifecycleThe software lifecycle has attracted a great deal of attention. It is at least generally accepted that theactivities in the lifecycle are analysis of requirements, design, implementation, testing anderror correction, extension. Unfortunately, the resultof identifying these activities has resulted in aschool of thought that the boundaries between theseactivities are fixed, and that they should besystematically separate, each being completedbefore the next is commenced. It is often argued thatif they are not cleanly separated, then you are notpracticing disciplined system development.

This view is incorrect; someone who writes aprogram straight away is actually doing all the stepsin parallel. It might not be the best way to do thingsin many circumstances, might or might not suit thestyle and thinking of different people, but this worksin some scenarios, and can be the methodology of

choice of disciplined thinkers. While that is anextreme example, the ideal way to work probablylies between that and a strictly regimentedenvironment that assigns different people or teamsto the lifecycle phases.

Some people can hold a whole problem andsolution in their head and work in a disciplinedfashion until the solution is complete. Mozart is saidto have composed this way, producing his last threesymphonies in as many months in 1788. Beethoventoiled far more over the production of his works,taking years to complete one symphony. Bothcomposers produced masterpieces. Mozart wrotemusic directly, whereas Beethoven wrote themes

and ideas in his famous sketchbooks. WhileBeethoven and Mozart had their own methods, theproduction of masterpieces depends on skill, not onmethodologies.

A view that is gaining acceptance is that thesoftware lifecycle should be an integrated process.Analysis, design and implementation should be aseamless continuum. The activities of the lifecycleshould progress in parallel to expedite softwaredevelopment. Facts found out only as late as theimplementation stage can be fed back into theanalysis and design stages. The object-orientedapproach supports this process. Artificial separationof the steps leads to a large semantic gap between

the steps. The transformations required to bridgesuch semantic gaps are prone to misinterpretation,time consuming and costly.

We should cease dependence on testing. This isnot to say that systematic or even random testing byan independent test group is not important, but weshould rely more on better techniques in thepreceding phases. Software testing can never provethe absence of error, it can only be used to detecterrors if they are there.

The same people should be responsible for allstages, so that they take responsibility for the system

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 41/63

C++?? 38

3rd Edition © Ian Joyner 1996

as a whole, rather than passing the buck and blamewhich occurs when analysts, designers andimplementors are different groups. This is not apopular view in traditional hierarchical managementstructures where organisational structure is prizedover quality and programmers get promoted todesigners who get promoted to analysts, andmanagers stay aloof from the technical process, just

making sure the old structure is maintained. Or evenworse, those who become analysts, designers andmanagers have little knowledge or experience of programming and large scale software engineering.Since the second edition of the critique, ScottAdams’ Dilbert comics have become widely knownas accurate comments on such organisationalproblems. Hierarchical management discouragespeople from feeling responsible for a product. Thisculture must radically change if we are to producequality systems.

We should have learnt from the extremes of SA/SD. Some quarters believed that methodologywas all important, while programming and

programming languages were unimportant. Arcaneand machine-oriented programming languagesstrengthened this attitude, concentrating on the‘how’ of computation, whereas the modellerscorrectly demand notations that express the ‘what’,in order to be implementation independent. Amodern software language supports the integrationof the activities of design and implementation bybeing readable, and problem-oriented. A languageshould be as close to design as possible. The needsand requirements of an enterprise can change muchmore rapidly than programmers can keep up,especially in a highly competitive and commercialworld.

So how does C++ fit into this picture? Well it isbased on C that was designed mainly as animplementation and machine-oriented language. It isan old language, that did not need to consider theintegrated lifecycle approach. C++ might have someof the trappings of object-oriented concepts, but it isan uncomfortable marriage of a problem-orientedtechnique with a machine-oriented language. Itaddresses implementation, but does not addressother aspects of the software lifecycle so well. SinceC++ is not so well integrated with analysis anddesign, the transformation required to go fromanalysis and design to implementation is costly.There is a large semantic gap between designlanguages and the implementation language.

We should have learnt from the structured worldthat this is the incorrect approach to the softwarelifecycle. But in the OO world we are again fallinginto the trap of dividing the lifecycle into artificiallydistinct activities of OOA, OOD and OOP, insteadof adopting an integrated approach. Modernlanguages provide a much more integrated approachto the complete software development process thanC++. C++ supports classes and inheritance and otherconcepts of object-orientation, but fails to addressthe entire software lifecycle.

Eiffel is specifically designed around theclusterfall model of the project lifecycle. In thismodel, several subparts of a project may be indifferent phases at any instant. It also recognises thatfeedback occurs from later phases to earlier phases.Eiffel itself is quite a good specification language.Its assertions and invariants are something like youwould see in a formal specification language like Z.

While not as comprehensive as Z, Eiffel’sspecification mechanisms suffice in most cases.(Bertrand Meyer was involved in the early work onZ). Thus you can use Eiffel as a documentationlanguage in phases as early as analysis. The problemof different notations in different phases, and error-prone translation between them is removed.

The mechanism that Eiffel includes to ceasedependence on testing is the assertion mechanism,integrated with exception handling. Organisationswill find it difficult to make significant progresstowards the higher levels of the SoftwareEngineering Institute Capability Maturity Model(SEI CMM) until techniques such as this in Eiffel

are in widespread use.Eiffel is also integrated with a graphical CASEtool called BON (Business Object Notation) forthose who feel more comfortable with classificationand component relationship diagrams. Mostimportantly, Eiffel and BON are based on the sameunderlying abstract concepts. Eiffel can be generatedfrom BON and vice-versa. This means you caneasily “reverse engineer” your text, but the majoradvantage is that your diagrams and your text arealways synchronised. There is no costlymaintenance when your program changes, anddiagrams have to be updated to reflect this fact.Thus Eiffel is a step towards seamless softwareengineering.

3.42 CASE ToolsThe previous section raises the question of CASEtools. [Madsen 93] has a good discussion ongraphical notation (18.8). BETA is a language thatcan be used for analysis, modelling and design. To acertain extent, this comes with any language thatsupports classes, as these are the elements of OOanalysis and design, but it is important to developthe language with analysis and design specifically inmind.

If you are using both graphics and textualnotations, it is important that both are based on the

same underlying abstract language: text andgraphics should represent the same concepts. Amajor problem with SA/SD was the graphicalnotations and programming notations were so farapart that costly and error-prone manual translationwas required between the two. Unfortunately, thishas set up the precedence in peoples minds thatgraphical and textual notations are necessarily farapart, and are surprised to see how close these are ingood object-oriented systems.

It should not be thought that graphics are highlevel, and text is low level; that is the nature of

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 42/63

C++?? 39

3rd Edition © Ian Joyner 1996

abstractions, not the tools or notations. In fact itshould be pointed out that text is a highly evolvedform of graphics; both forms of information enterour brains through our eyes Because of the nature of graphical notations less detail can be shown. Withan integrated editor detail in text can be suppressed.In identifying classes during analysis, it reallymakes no difference whether you document them as

a series of graphical boxes with class names in themiddle, or a textual list of class names. In fact manypeople will find the list easier to work with and laterread. At any stage the notations should beinterchangeable. In some cases the graphicalnotation will abstract away details, which is anadvantage, when you don’t want to see the details.As you add details though, graphical forms becomeunwieldy, and text is easier to manage.Unfortunately, many sectors of the industry havebecome convinced that graphical forms are moreformal and result in magically better designs thattext equivalents.

Graphics and text are best in an integrated

environment. A programmer may have a classdiagram as a starting point, like GUI file icons.Selecting a class will expand the class so that theinterface of the class can be seen. At a differentlevel, internal features of the class might be seen.Eventually, a level where text is seen is reached.The major failing of most CASE tools is they do notsupport this level of seamless integration. For themost benefit they should flow into the programminglanguage. So called ‘visual’ environments do littlebetter than putting program text in a GUI window.

Why bother with graphics then? For the simplereason that looking at the same problem in differentways aids understanding. It is also a matter of taste.

Some people will find they understand graphicsbetter, and some text. It is a good idea to cater forpersonal tastes, as long as there aren’t too manyoptions, in which case everyone will end upspeaking their own language, and there will be noeffective communication, a tower of Babel. But thishas already been the case in the industry, as designmethodology notations are far apart, with theanalysts/designers not wanting to read programs,and programmers not wanting to read structurecharts and data flow diagrams.

A common design method with C++ is to useOMT (UML) or some equivalent methodology.However, the object models are different as thegraphical and textual languages are not based on thesame underlying abstract language. Thus there is asemantic gap between the text and graphics. Thisresults in more costly and error-prone development.But then as the OMT people have said “Eiffel isarguably the best commercial OO language in termsof its technical capabilities.” [RBPEL91], p327. Theobject model of Eiffel is certainly closer to OMTthan C++.

In conclusion, if CASE tools and graphicalnotations are to be of use, they and the programming

language must be based on the same abstractconcepts.

3.43 Reusability and CommunicationReusability is a matter of communication.

Clear communication is a courtesy concern. Inorder to use a software component, you must be ableto understand it. The writer must communicate thepurpose, intent, and correct usage of the componentto the client. In the object-oriented world, clear andconcise definition of software modules is not a merenicety, but essential for reusability. Arising out of the issue of reusability is extendibility. In order tomaximise the reuse of software, it must often betailored for new applications. The client programmermust decide whether a software component issuitable for a new task, and if so, what is the bestway to extend it?

Communication is aided by having integratedtext and graphics environments, where the concretelanguages of both are based on the same underlyingabstract languages, or object models.Communication is also dependent on clear and cleansyntax.

As C/C++ suffer from arcane and cryptic syntax,it does not support the goal of clear communication.

Java cleans up a fair bit of C/C++. The mess thatis caused by the preprocessor is removed. However,Java still suffers from some of the deficiencies of Cin this regard.

Eiffel has been designed with communication inmind, and is not bound by the shackles of C syntax.It borrowed from the clean syntax of Ada. Alongwith the Eiffel syntax were designed styleguidelines, so the Eiffel syntax lends itself to a clearstyle.

Eiffel also has utilities like short , where theabstract interface of classes can be extracted fromthe full details.

Eiffel provides an extra significant mechanism,that of integrated assertions. The short tool willextract the assertions with the interface descriptions.This has been described in the section onprogramming by contract. Programming by contracthelps decide whether a class is useable in a newsituation, and then how to use it, so this is animportant tool for communicating the purpose,intent and correct usage of a software module. Thusassertions are very much a courtesy concern.

Reusability is well supported with clearcommunication in Eiffel.

3.44 Reusability and TrustReusability is a matter of trust.

Building trustworthy components is a safetyconcern. Trust results from confidence that safetyconcerns have been met. If you do not haveconfidence in a software component, then you won’twant to reuse it. You could doubt that the softwarecomponent provides enough functionality, or correctfunctionality. You could doubt that the component

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 43/63

C++?? 40

3rd Edition © Ian Joyner 1996

is efficient enough, or worse it might fail. As somany traps in C++ result in ‘bugs’, it is difficult totrust a software module, so it is less reusable.

In the real world of reusability, the ideal of trusting programmers is inappropriate, and results inless trustworthy software; in reality, customersdoubt the claims of suppliers. It is the onus of thesupplier to prove their claims, and thus

trustworthiness of the software. The client is notrequired to trust the supplier’s programmers.Potential clients of a software component, requireassurance that the component is trustworthy.

Trusting programmers is against the commercialinterest of both parties. This is not to cast dispersionon programmers, but merely recognises thatcomputers are good at performing mundane tasksand checks, but people are not. If people were goodat such things, we would not need computers in thefirst place.

Even though you might not trust yourprogrammers, this is not an excuse to employanything but the best skilled programmers, and

programmers should also be given the best training.Consider a Stradivarius violin: it will sound bad inthe hands of a bad violinist. But a good violinist willinsist on a Stradivarius, rather than a cheap brandwhere he won’t sound his best. In computing, wefrequently argue whether it is the tools or theprogrammers. It is a combination of the two; if either is lacking, trustworthy software will notresult.

Java “eliminates entire classes of programmingerrors that bedevil C and C++ programmers” [Sun95]. This means that you can better rely onexternally developed Java packages.

Eiffel also is not bedevilled by the same classes

of errors. Thus you are more likely to producesoftware that can be used in other contexts, and beable to find software that can be reused in yourcontext.

Eiffel assertions are also important here. Asassertions are checked at run time, they ensure thatthe software is working correctly, so the level of trust in external components is higher, and you reusethem with more confidence.

3.45 Reusability and CompatibilityDifferent compiler implementations need to becompatible in order to realise reusability betweenlibraries and components. Different C++ compilersgenerate different class layouts, virtual functioncalling techniques, etc. The name encoding schemesused for type safe linkage can also be different. If two different compilers generate different run-timeorganisations, then different name encodings aredesirable as it will prevent two incompatiblelibraries from being linked. The C++ ARM (p122)states: “If two C++ implementations for the samesystem use different calling sequences or in otherways are not link compatible it would be unwise touse identical encodings of type signatures.”

This can be solved in two ways: firstly, a libraryvendor could provide the entire source of a libraryso it can be compiled with the customers compiler;if the sources are proprietary the vendor will need aseparate release for every environment, and everycompiler in that environment.

Because of this problem a strong case exists fora universal intermediate machine readable

representation of programs. Interestingly, somesystems are already using C as a ‘universalassembler’, notably AT&T C++ and Eiffel. But thiscannot solve the above problems of compatibilitybetween components without a standardisation efforton run time layouts and name encoding schemes.

An important feature of Java is that it isarchitecture neutral as Java compilers produce bytecode instructions for a virtual machine. Javaprovides a “universal intermediate machine readablerepresentation of programs” as I called for in thispaper’s second edition.

Eiffel implementations provide a high level of source code compatibility. However, the generated

C from different implementations can have differentobject layouts. Thus a class library will have to berecompiled if it is to be used in a system compiledwith a different vendors implementation.

Another form of incompatibility betweenlibraries is incompatibility of type definitions. Aglaring example in C++ is the number of ways thesimple type boolean can be defined. For more onthis see the section on booleans.

3.46 Reusability and PortabilitySince true OOP ensures that objects are looselycoupled to the external environment, portability todiverse environments is possible. C is highlycoupled to Unix style environments, and as such isnot particularly portable to diverse environments.

Java is also the winner in this category, due toits virtual machine, and removal of pointers. Eiffelcode is also highly portable, but you are currentlyconfined to systems where Eiffel compilers exist of which there are many. As most Eiffel compilersgenerate C, you can port the generated C toplatforms where there is no Eiffel compiler. WithJava, only a virtual machine interpreter needs to beavailable on the system in order to run Javaprograms.

As the Java virtual machine seems to besufficiently semantically rich, it could be that otherlanguages target the Java virtual machine, and that itbecomes a universal machine code. Such a marriagemight not be as easy as it appears, if the objectmodels of different languages are sufficientlydifferent from the Java model. Sun does seem tohave kept the virtual machine independent of physical object layout, and any assumptions thatwould make this too hard.

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 44/63

C++?? 41

3rd Edition © Ian Joyner 1996

3.47 Idiomatic ProgrammingThe ability to program in different idioms is arguedas a strength of C++. Idiomatic programming,however, is a weak form of paradigmaticprogramming; it is programming in a paradigmwithout necessarily having compiler support for thatparadigm. The compiler cannot check forinconsistencies with the idiom, or paradigm. Defines

can often be used to invent idioms. Anyone who hasattempted to do object-oriented programming in aconventional language using defines will realise thatit is impossible to realise the benefits easily, if at all,without compiler support.

Both Java and Eiffel are strongly object-oriented: the idiom is OO. You don’t have to bringtogether various sub-projects each of which mighthave used their own favourite idiom.

3.48 Concurrent ProgrammingThe object of concurrent programming is thatcomputing resources can be harnessed to efficientlycompute problems that would otherwise beinefficient to compute using a single processor. Inthe next ten years multiple processor arrays thatexecute programs concurrently will likely becomecommon. Concurrency requires much cleanerlanguages, than the single processor languages of today.

Object-oriented concepts support concurrentprogramming. Objects can execute state changingcode independently of each other. Concurrentprogramming will be enabled by the division of thestate space of a system into modules to achieve ahigh degree of independent processing. Objectsprovide a scheme to cleanly divide state spaces. Thedemand that everything be divided into looselycoupled modules, that only interact through welldefined interfaces might be perceived as inefficient;but it is precisely this scheme that will mean thatconcurrent solutions can be developed efficientlyand transparently to the programmer.

Concurrency should be transparent to theprogrammer, as concurrency is a low levelimplementation consideration; concurrency is how acomputation is done, not what is to be computed.However, there are examples where concurrency ismanifest in the problem domain, such as manysimulation problems like multiple queues, forexample check-outs in a supermarket. Theimplementation issue of concurrency is howprocesses are allocated to processors. Theprogrammer should not be concerned with this,rather what is to be computed, not how. Howsomething is computed is the concern of the targetenvironment, ie., the compilers, operating system,and hardware.

The aim of concurrent processing is to keep allthe processors in a processor array as fully utilisedas possible, so that processor resources are notwasted. There is nothing more mysterious toconcurrent programming than the efficient use of

resources. Keeping all processors busy is aninherently dynamic problem, which the programmercannot determine statically at compile time. All theprocessors can be kept busy, as long as there areenough threads in the system.

In concurrent programming, a thread is a unit of sequential execution. Concurrency is achieved bythe splitting of threads. A thread can be split when a

state changing routine is invoked, but not a valuereturning function, because it must wait for thevalue. State changing routines can easily be invokedon another processor. Object level granularity seemsto be a natural candidate for concurrent processing.An object can have only one update thread at a timeto avoid simultaneous update problems. Other levelsof concurrency are instruction level, and task orprocess level. Task or process level is the level usedin conventional multi-processing systems currentlycommercially produced, and instruction level isquite difficult, best left to instruction pipelines.

Object level is natural for the programmer, andhas the advantage that a programmer can implement

a system without taking into account parallelprocessing at all. The same program will run andproduce identical results irrespective of whether thecustomer is running a single processor, or aprocessor array. This way the programmerconcentrates on the model and design of theproblem, not on deployment concerns.

Side effects must be avoided in concurrentsystems. Suppose a computation depends oncombining the results of two functions f and g, suchas f + g. f and g are parameters to the + function.Routine parameters can be computed concurrently,as long as the computation of each causes no sideeffects. If f and g are independent, then they can be

computed concurrently. If however, f produces sideeffects that g depends on, they must be computedsequentially.

C++ does not preclude the use of a globalenvironment. Access to shared global datapotentially causes a thread to lock, and if many suchaccesses occur, the advantage of concurrency is lost.This is because updates to a global environment areside effects. Programming in such an environmentrequires complex locking mechanisms to ensure thatthings happen in the correct order. Locks are ratherlike waiting for a plane to take off when it has towait for another connecting flight. This cannot beentirely avoided, but should be reduced as much as

possible.It might not be impossible to implementconcurrent processing in C++, but it is difficult as inmany ways C++ is not suited to concurrentprocessing.

Java provides threads. It also removes C featureslike globals that are problematic to concurrency.

Eiffel has a recommendation [Meyer 96c] thatextends Eiffel with a single keyword separate toprovide concurrency. Both Java and Eiffel havesimple concurrency mechanisms due to their cleanerbase than C++.

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 45/63

C++?? 42

3rd Edition © Ian Joyner 1996

3.49 Standardisation, Stability andMaturity

Object-orientation is now nearly 30 years old, sinceSimula 67. Smalltalk is about 20 years old, Ada 95is only one year old, but based on Ada 83, which isabout 13 years old. C++ is 13 years old. Eiffel is 10years old, and Java is just one year old.

The age of a language does not relate to its

stability and maturity. Java is the youngestlanguage, but Java appears to have a well thoughtout and stable language base, also having acomprehensive set of OO libraries. Thus Java is off to a good start, but only time will tell. It already hasquite a number of books.

Ada 95 is one year old. But that is one yearsince the standard was ratified, so it is a good dealolder than a year. Ada 95 is the product of anISO/ANSI/DoD standard. Thus Ada 95 vendorshave a very stable base from which to implement.This gives Ada 95 a good start over other languages,where there might be implementations, but they areshooting at a moving target.

Eiffel is not subject to the ‘formal’ ISO/ANSIstandards; it has its own non-aligned standards bodyNICE (Non-profit International Consortium forEiffel). Eiffel is now in its third incarnation, Eiffel 3that is fully described in Eiffel: The Language[Meyer 92], the Eiffel equivalent of the C++ ARM.However, the definition of Eiffel 3 has been verystable since 1992, requiring only a few extra validityrules, and small clarifications: Eiffel is probably thebest designed language ever intended forcommercial use. The largest change to the languageis now under consideration, which is to add theseparate keyword to allow support for concurrentand distributed processing. This will not affect

existing programs, and early releases of implementations with this mechanism are nowavailable. Eiffel also has a standard library. Thestandard library is more changeable than the baselanguage, but is also under the control of NICE.Thus Eiffel has attained a great deal of maturity over10 years, and the standards are very stable. Thisgives Eiffel a considerable advantage in thatlibraries are much easier to update to address newand changed requirements than compilers.Therefore, Eiffel should evolve more quickly intonew problem domains, without the traditionalresistance from compiler vendors.

The most serious problem that Eiffel has faced

in the past was stability of implementations. AsEiffel is an ambitious language and environment,many new and difficult concepts have beenpioneered and made into industrial strengthpackages. Eiffel is very demanding on compilers,which need to do things like global analysis, whichis an issue that C++ conveniently avoids. Eiffel doesnot concede to compromises which place burdens onthe programmer in the same way that C++ does.

However, stable forms of Eiffel environmentsare now becoming widely available. In 1996 TowerTechnology has released version 2 of its compiler

and environment, and ISE has announced version 4of its environment, which addresses many issuesthat users did not like previously, and now includesmenus and other facilities, which gives it a moreMacintosh/Windows look and feel. SIG Computerhas also announced its Visual Eiffel for releaseOctober 1996. There is also an independentexperimental version known as SmallEiffel, which

can be downloaded for free.Another problem that Eiffel has had is the lack of titles. [Meyer 88] is the classic book on OO,however, it is based on Eiffel 2.0, not version 3.Meyer’s next book “Eiffel: The Language” [Meyer92] is the language lawyer’s reference, but it ispossible to navigate for an overview. However, thereare now over ten titles on programming in Eiffel,quite a few of which are used to teach universitycourses on OO.

Smalltalk is now a widely used language, andhas proven to be very effective in someenvironments. Different implementations of Smalltalk do not share libraries, and do not

interoperate.Out of all the languages here, C++ although 12years old, provides the fastest moving target forvendors. It is claimed to be standardised, as it issubject to ANSI/ISO standardisation, but this work is still very much in progress. You can check statusof the standard on the X3J16 WEB page in theWEBliography). The number of issues to beaddressed by the committee keeps increasing, ratherthan decreasing. C++ was submitted to thestandardisation process too early, and the committeehas had to do too much design work that shouldhave been done before C++ was submitted to thestandardisation process.

The committee hopes to progress the standard toCD (Committee Draft) this year (1996). The FAQshows a timetable which will produce an IS byDecember 1998 (see WEBliography:http://reality.sgi.com/employees/austern_mti/std-c++/faq.html#B8). After IS is achieved, it willprobably be several more years before a significantnumber of vendors are fully compliant. By thatstage, users will probably be clamouring for morefeatures and fixes to old problems. I have alreadyheard stories of C++ tool vendors complaining thatthe standard is too horrendous to understand, andthen to implement anything compliant.Standardisation should stabilise the specification,

but C++ has continued to become less stable. Thefact that the C++ standard is so unstable indicatesthat the C++ committee realises there are manyshortcomings in C++ that they must rectify. Thereare many flaws that the committee knows about thatI do not cover in this critique, but also many of theflaws that are covered in this critique, the committeehave no intention of addressing, as that would break too many existing programs and C compatibility.

In the preface to [Stroustrup 94], BjarneStroustrup writes “C++ is still a young language.Some of the issues discussed here are yet unknown

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 46/63

C++?? 43

3rd Edition © Ian Joyner 1996

to many users. Many implications of decisionsdescribed here will not become obvious for years tocome.”

Coming to consensus in the C++ world is adifficult task. [Stroustrup 94] states this frustrationas “Dealing with stubborn old-time C users, would-be C experts, and genuine C/C++ compatibilityissues has been one of the most difficult and

frustrating aspects of developing C++. It still is.”Many comments in [Stroustrup 94] show thatC++ is still a moving target. Garbage collection ismentioned as “when (not if)”. Thus when GC isfitted to C++, developers will be faced with quite atransition in paradigm. All of this uncertainty inC++ might keep the programmers busy, after allmany of them want to code exclusively in C++,while ignoring all else; but it will be very costly forthe companies that are locked into C++.

There are still unresolved things the X3J16committee must sort out, especially in the area of Ccompatibility. [Stroustrup 94] says “The“compatibility wars” now seem petty and boring,

but some of the underlying issues are stillunresolved, and we are still struggling with them inthe ANSI/ISO standards committee. I stronglysuspect that the reason the compatibility wars weredrawn out and curiously inconclusive was that wenever quite faced the deeper issues related to thediffering goals of C and C++ and saw compatibilityas a set of separate issues to be resolvedindividually.” Since C compatibility results in somany problems, serious consideration should begiven to this basic tenet of C++.

The C++ community seems to think using afundamentally flawed tool is acceptable and that therest of the world must wait for them to straighten

these issues out, which in many cases isn’t evenpossible. It is also a hidden cost to companies thattheir programmers must continually keep up to date,and abreast of the arguments for and against certainconstructs. Many other languages have solved theseproblems.

As a postscript to this section, I will remark thata lot of argument for or against particular languagesseems to come from people who believe that therewill be an eventual winner in the evolution of languages, and they want it to be their favourite, sowill fight for dominance. I can see no evidence thatthis will happen. I think new languages willcontinue to be invented: some will be based on

continuing mistakes from old languages whileadding new features for compatibility; others willavoid previous errors while adopting newparadigms. I can’t see that the programminglanguage world will ever become stable. If people inthe industry can accept that, then we will haveprogrammers that are more amenable to changelanguage, being able to use the language that is bestsuited for the purpose, and the maturity of languagecriticism will improve, as we see each language as apassing phase, to which we owe no long termallegiance.

3.50 ComplexityThere are several kinds of complexity. This critiquefocuses mainly on the complexity of the C++language itself. When considering complexity, oneneeds to consider the complexity of the developmenttask as a whole. The complexity of the languagemight only be a small part of that.

Apart from the language, we need to consider

the programming environment, that is editors, toolsfor example make, etc., the methodologies andtools, and the supporting libraries.

With C++ the conventional wisdom is often touse a methodology such as OMT. Here the conceptsof the methodology do not exactly match theconcepts in the programming language. Thus youhave a semantic gap, where translation must occur.This translation is costly, and frequently ends inspecifications that do not match what was eventuallyimplemented.

Both Eiffel and BETA see it as important todevelop their methodologies and graphical notationsbased on the same underlying concepts. The

importance of this integrated approach should not beunder-appreciated.As for environments, [Stroustrup 94] has the

following to say: “Every language in nontrivial usegrows to meet the needs of its user community. Thisinvariably implies an increase of complexity. C++ ispart of a trend towards greater language complexityto deal with the even greater complexity of theprogramming tasks attempted. If the complexitydoesn’t appear in the language itself, it appears inlibraries and tools. Examples of languages/systemsthat have grown enormously compared to theirsimpler origins are Ada, Eiffel, Lisp (CLOS), andSmalltalk. Because of C++’s emphasis on static type

checking, much of the increase in complexity hasappeared in the form of language extensions.”“C++ was designed for serious programmers and

grew to serve them in the increasing large andcomplex tasks they face.”

P.J. Plauger in [Plauger 93] argues that thecomplexity of C++ has put it on par with PL/I, Ada(83) and Algol 68. He does not accept thecomplexity in C++ as a good thing. Criticising thecomplexity of Ada is somewhat unfair. An amountof Ada’s complexity is due to its support of multitasking and real-time programming. Simulaalso has facilities for co-routines and processes, andAda and Simula are reasonably unique for their

inbuilt support of these facilities. In the 1980s, theneed for such facilities was not widely recognised.However, the need for concurrency and distributionis now becoming recognised.

Another feature of Ada that might contribute tothe perception of complexity is genericity. Again thecharge that this makes the language over complex isbased on not understanding genericity. I havealready covered this topic in the section ontemplates. Thus Ada has been criticised for beingcomplex, but most of this criticism is due to not

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 47/63

C++?? 44

3rd Edition © Ian Joyner 1996

understanding essential features such as genericityand concurrency.

Many C programmers have been guilty of dismissing features they don’t understand ascomplexity, and Ada has been a favourite target. Iam not saying that Plauger is in this category, as hemakes some valid points about Ada. But theaccusation of complexity against Ada should not be

overstated as it has too frequently emotionally beenin the past. In the computing industry, there is a lowlevel of understanding and experience that one musthave before becoming and expert or vocal critic,particularly of languages like Pascal and Ada.

C++’s complexity is not solely due to static typechecking. Eiffel is more strongly type checked thanC++, but doesn’t suffer from the same complexityproblems.

As for the environment. The burden of environment is far less for the cases of Eiffel, Javaand Ada 95. In Eiffel, a separate simple languageexists, LACE to specify to the compiler how tocompile the program. This contains such things as

environment variables, debug and other options, etc.It also provides the basis for separation of concernsso that environmental details are completelyremoved from the Eiffel language. Eiffel is alsointegrated with complete editing and developmentenvironments.

Java has removed such environmentalconsiderations as #include and make . EdmondSchonberg writes that the environmental baggagefor Ada and Ada 95 is far smaller than C++ (seeWEBliography for his Ada contrast to C++).

The Eiffel libraries are very large andcomprehensive; but this only reflects the richness of data structures that exist, and the number of

application domains. Eiffel libraries are available fornetworking, compiling and parsing, Windowsprogramming as well as platform independent userinterfaces and many other things. The Eiffel librariessimplify naming complexity by standardising thevocabulary between classes. For example, put isused to enter an item in any collection data structurelike ARRAY , LIST , QUEUE , and even STACK wherethe routine would normally be named push . Thelibraries enable the complexity of specific domainsto be removed from the language, which is simpleand yet general purpose.

Smalltalk also has a large library, which extendsan otherwise small and simple language. Classes

that a programmer adds also become part of theSmalltalk environment.Java also provides a comprehensive library to

deal with many aspects, including java.net, java.awt(abstract windows toolkit), etc. Eiffel, Smalltalk andJava do not ignore the issue of complexity; they putit where it should be: in the libraries. In terms of complexity, they implement Stroustrup’s principlethat “what you don’t use, you don’t pay for.” InC++ you pay very much for complexity, as it is inthe language.

C++ can to some extent be extracted from thecomplexity of its environment. But as long as themechanisms of #include persist, theenvironments that C++ is ported to will have toadapt to the C/Unix way of doing things. Where theenvironment is separate from the language, there isno environmental adaptation that needs to be done,and less retraining of programmers for each

environment they need to program in.I can accept that C++ was designed for seriousprogrammers. However, Ada 95 and Eiffel are bothdesigned for the serious software engineer. (Javaremains to prove itself in this arena.) Eiffel inparticular shows that complexity can be dealt within a serious industrial strength software engineeringenvironment.

Complexity is not the necessary companion of seriousness. This does not ignore the complexity of any application domain; in fact it enables you tofocus on the complexity of the programming task inhand, not on the complexity of the tool.

3.51 C++: the Overwhelming OOL ofChoice?This headline comes from Cutter Information Corps“Object-oriented Strategies” May 1996 edition.Based on their findings, C++ accounts for 80% of all OOLs, with Smalltalk running a distant second at11%. They claim that in 1995 OO softwaredevelopment products hit $1.3 billion. However,let’s examine how C++ is used: many Cprogrammers have not wanted to touch C++, butthey do use a C++ compiler to compile their C. Thisgreatly exaggerates the market penetration of C++and the size of the OO market, so it is impossible todetermine the true market penetration of OO. You

are not doing OO just because you are compilingwith C++.Microsoft and Borland have put most of their

development environment energies into C++, so thismakes it attractive to buy a C++ environment, evenif you are just programming C. Probably the truenumber of C++ installations being used for OOwould be between 10-50%, which cuts down thesize of the OO market by a large amount, the size of C++’s predominance in that market, and means theother OOLs in the market have a much highersignificance than Cutter makes out. Smalltalk andEiffel are pure OOLs, so every one of their sales youcan count as an OO installation, whereas the same is

not true of C++. Measured C++ sales are riding onC’s success. C++’s success is less thanoverwhelming. It is a marketing success, rather thana technical or programming success. Companiesusing C++ are paying for it with longer cycledevelopment times, and less reliable end product.

One way a manager might perceive C++ to be awinner is the sheer number of books one sees in abookshop on C++. This is matched by a hugenumber of courses. An observation about the natureof many of these books is that they are often titledsomething like “How to build a widget in C++,” or

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 48/63

C++?? 45

3rd Edition © Ian Joyner 1996

“Compiler Construction in C++.” “Books appearlike mushrooms after rain” [Plauger 93].

The mushrooming book market is a great boonfor publishers, as it implies that for every possiblesoftware artefact you can build, they can publish abook about it in every possible programminglanguage. All you really need is the books“Programming in C++,” and “How to build

widgets,” or “Compiler Principles andConstruction.” Then your programmer needsexperience, lots of it. Don’t be fooled by this trick toget a high title count.

Many C++ books are on how to avoid the trapsand pitfalls, and develop rigorous coding standards,which might appeal to management as the solution,but they don’t solve the root cause of the problem.Making sure everyone is well trained and versed inthese style standards is an expensive and usuallyineffective band-aid measure, especially wheredifferent companies have different standards andexpectations, so you need to retrain every newrecruit, who will probably decide they don’t like

your way of doing it anyway, and leave after a shortperiod. Of course you can satisfy yourself that hisdissatisfaction was due to his inappropriateness foryour organisation, which is better organised thanmost. After all, you are ISO 9000 accredited and areturning out a very successful line of ‘concrete life-

jackets’ (a Tom Peters quote).[Sakkinen 92] observes the “Endemic C++

Culture.” He notes that too many courses on“design” have the appended clause “with C++.” Thisis because C++ has its own curious terminology,which is in many ways different to the rest of theOO world. He makes a case that concepts andprinciples should be taught, then how to map them

onto any particular language.Of course books are aimed at differentaudiences: professionals versus those who justprogram for a hobby; those who have an academicinterest in languages; implementors of compilersand other language processing tools, who needformal non-ambiguous statements about how thelanguage works; beginners versus those for whomthis is their fourth or fifth language. C++ should notbe for beginners, as it is better to learn the principlesfrom a clearer language than be confused by what allthe syntactic knobs and dials , and superfluousconstructs do in C++.

As for courses, C++ has proven so difficult to

learn that you need lots of courses. Not only do youneed to learn the language, but the complexities of the environment add an even more substantialoverhead. It will probably be best to start on C++with a course. However, with simpler languagessuch as Java and Eiffel, buying a good book, andself experimentation will quickly cover every aspectof the language. It is a bonus if you can get a course,but it is not essential to get started.

4. Generic C CriticismsThese criticisms apply to the C base language, but ingeneral adversely affect C++. R.P. Mody [Mody 91]gives an excellent general criticism of C. Mody saysthat to properly understand C you must understandthe insides of the compiler, giving many examplesof how C obscures rather than clarifies softwareengineering. He concludes that he is “appalled at the

monstrous messes that computer scientists canproduce under the name of ‘improvements’. It is toefforts such as C++ that I here refer. These artefactsare filled with frills and features but lack coherence,simplicity, understandability and implementability.If computer scientists could see that art is at the rootof the best science, such ugly creatures could nevertake birth.”

4.1 PointersC pointers are a low level mechanism that shouldnot be the concern of programmers. Pointers meanthe programmer must manipulate low level addressmechanisms, and be concerned with lvalue andrvalue semantics, which are machine oriented andnot problem oriented as you would expect of a highlevel language. A compiler can easily handle suchissues without loss of generality or efficiency.Memory models of different environments oftenaffect the definition of pointers. Memory modeldetails such as near and far pointers should betransparent to the programmer.

The programmer must also be concerned withcorrect dereferencing of pointers to accessreferenced entities. Use of pointers to emulate byreference function parameters are an example. Theprogrammer has to worry about the correct use of &s and *s. (See the section on function parameters.)

Pointer arithmetic is error prone. Pointers can beincremented past the end of the entities theyreference, with subsequent updates possiblycorrupting other entities, which is a major source of the undetected inconsistencies, which result inobscure failures, discussed in the section oncorrectness. In the STL library, iterators areprovided as the generalisation of C pointers foraccess to elements of structures such as arrays.

Programmers can by-pass encapsulation withpointers; C undermines OOP by providing amechanism where state outside an object’sboundaries can be changed. Since pointers areintrinsic to writing software in C this exacerbatesthe problem. Pointers as implemented in C make theintroduction of advanced concepts like garbagecollection and concurrency difficult.

Another consideration is that dynamic memoryimplementations vary between platforms. Someenvironments make memory block relocation easierby having all pointers reference objects via a masterpointer which contains the actual address of theblock. The location of the master pointer neverchanges, so relocation of the block is hidden fromall pointers that reference it. When the block is

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 49/63

C++?? 46

3rd Edition © Ian Joyner 1996

relocated, only the master pointer needs to be up-dated.

On the Macintosh, for example, the doubleindirection mechanism of ‘handles’ facilitatesrelocation of objects. Object Pascal makes handlestransparent to the programmer. This is similar to theUnisys A Series approach where object descriptorsaccess target objects via master descriptors that store

the actual addresses of objects. On the A Series thisis transparent to programmers in all languages, asthis transparency is realised at a level lower thanlanguages. The A series descriptor mechanism alsoprovides hardware safety checks that mean thatpointers cannot overrun, and arrays cannot beindexed out of bounds. C cannot be implementedparticularly well on such machines, as C’s pointermechanisms are lower level than the targetenvironment.

Simpler environments might not provide objectrelocation, so double indirection would be anunnecessary overhead. In order for programs to beportable and efficient in different target

environments, such system details should be theconcern of the target compilation system, not of theprogrammer.

C’s pointer declaration syntax causes anothersmall problem:

int* i, j;This does not mean, as might be easily read -

int *i, *j;

but

int *i, j;

and should be written thus to avoid confusion.

Java has abolished pointers as “Most studiesagree that pointers are one of the primary featuresthat enable programmers to put bugs into their code.Given that structures are gone, and arrays andstrings are objects, the need for pointers to theseconstructs goes away,” [Sun 95]

Eiffel also has no pointers only objectreferences. In Eiffel, the exact referencingmechanism does not matter. For example in theexpression x.f the reference x might be a pointer toan object in the same address space, or it might bean Internet address of an object. References enablethe location and access method of an object to betransparent.

4.2 ArraysPage 137 of the C++ ARM notes that C arrays arelow level, yet not very general, and unsafe. Page212 admits, “the C array concept is weak andbeyond repair.” Modern software production is lessdependent on arrays, especially in the object-oriented environment. The trade off to be optimal,rather than general and safe no longer applies formost applications. C arrays provide no run-timebounds checking, not even in test versions of

software. This compromises safety and underminesthe semantics of an array declaration, ie., an array isa particular size, and can only be indexed by valueswithin the bounds of the array. The array size mightnot be determined at compile-time, but dynamicallyat run-time. An index to an array is a parameter inthe domain of the array function. An index out of bounds is not a member of the domain, and should

be treated as severely as divide by zero. But in Cthis is another significant source of undetectedinconsistency, which can result in obscure failures.

C has no notion of dynamically allocated arrays,whose bounds are determined at run time, as inALGOL 60. This limits the flexibility of arrays.You cannot resize C arrays. Multidimensional arraysare only really one dimensional. You cannotindividually resize the rows of a multidimensionalarray. The C definition of arrays compromises bothsafety and flexibility.

There are many ways you can undermine arraysin C and C++, as an array declaration is really justequivalent to a pointer. The following example

comes from [GWS 94]:char *str = “bugy”;

then the following are true:

0[str] == ‘b’;*(str+1) == ‘u’;*(2+str) == ‘g’;str[3] == ‘y’;

This is amazingly flexible syntax for something asinflexible as C arrays, which is against Meyer’s“Principle of Uniqueness” (see introduction),providing several ways to do the same thing, butstill not doing it particularly well.

The unsafeness of C arrays is shown in the nextexample:

#include <stdio.h>#include <string.h>main (){

char str[] = "TEST";char *p = "TEST2";const char str3[] = "TEST3";char *p3;

printf ("str = %s p = %s str3 =

%s\n", str, p, str3);p3 = &str;strcpy (p3, "some junk");printf ("str3 = %s\n", str3);str[6] = 'X';

printf ("str = %s p = %s str3 =%s\n", str, p, str3);

}

The results (at least from my C compiler) are:

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 50/63

C++?? 47

3rd Edition © Ian Joyner 1996

str = TEST p = TEST2 str3 = TEST3str3 = junk str = some Xunk p = TEST2 str3 = Xunk

One view of arrays is just another object-orientedentity which should be treated in an object-orientedmanner as a class of data structure. It should haveinterface definitions, and consistency checksinherent in object-oriented systems. Another view isthat an array is an implementation of a function,where pairs of values explicitly map the domainuniquely to the range, rather than being computed.This suggests that Algol was incorrect insyntactically distinguishing arrays by using squarebrackets. An array just maps the input argument (theindex) to the value stored in that location in thearray.

[Ince 92] considers that arrays and pointers neednot be relied upon so heavily in modern softwareproduction, as higher level abstractions such as sets,sequences, etc., are better suited to the problemdomain. Arrays, and pointers can be provided in anobject-oriented framework, and used as low levelimplementation techniques for the higher level dataabstractions. Ince suggests that arrays and pointersshould be regarded in the same way as gotos in theseventies. He suggests that languages such as Pascaland Modula-2 should be regarded in the same wayas assembler languages in the seventies. This applieseven more to C and C++, because pointers andarrays are far more intrinsic in the use of C andC++, with lower level, less flexible arrays. AlthoughPascal arrays are weak compared to those of ALGOL, they are still much better than C arrays.

In both Eiffel and Java, arrays are first classobjects. Both languages have no need of the sizeof function. In Java to get the size of an array you usemyArray.length . In Eiffel this is my_array.count .Arrays can also be resized.

Both Eiffel and Java provide bounds checkingon arrays. Java’s checking is built-in. Eiffel’schecking is integrated with the assertion mechanism.

Eiffel goes a step further in array elementaccess. You access an element with the itemfunction as follows:

v := my_array.item (i)

This can also be accessed by an infix operator, @ :

v := my_array @ i

The item function is defined as:

item (i: INTEGER) : Grequire

lower <= i ;i <= upper

This shows how Eiffel’s assertion mechanism isused to document semantics in the interface, as wellas for a checking mechanism.

4.3 Function ArgumentsArguments are a fundamental mechanism for reusein software construction. Without arguments youwould be forced to write a different routine for everypossible input parameter. Arguments allow onealgorithm to be reused on sets of input values.

Arguments pass routines simple values (by-value arguments), or references to entities (by-refer-ence arguments). (Actually, there are morepossibilities than this. [Hext 90] is an excellent texton the possibilities.) Arguments are inputs toroutines, and should not be changed. When memorywas expensive, reusing parameter space couldconserve space. Changing arguments, however, issemantic nonsense, and most languages get thiswrong.

By reference arguments enable a routine tochange the value of an entity external to the routine.Such updates beyond the environment of a routineare side-effects. This introduces a mechanism of updating the state space, other than straightassignment (although the routine can use assignmentto achieve the ‘dirty deed’.) The problem is that thestate of an object can be changed without using thewell defined interface of the object, so encapsulationis compromised. By-reference arguments should notbe used to change external entities. Values shouldonly be passed to external entities by the returnvalue of a function. Semantically, this is different toassignment to a reference parameter; data flowsthrough the program in one direction, in viaarguments, and out via return values.

Mathematically this maps a value of an input type toa value of an output type. Both input and outputtypes can be compositions of other types, ie., f : I1 x

I2 x ... Im -> O1 x O2 x ... On . Abstract data typescan be used to design such systems. This will alsohelp target environments to increase parallelism andconcurrency in a way transparent to programmers.

In object-oriented programming, by referencearguments are used to pass the original object, not acopy. The called routine, however, should notchange the state of the referenced object. Onlycalling a routine in the passed objects interface canchange the state, although introducing side effectsinto arguments like this is dubious and should be

avoided. Passing objects by-reference has thedesired effect of the object being given to you,without being yours to change, although you caneffect change in the object. C++ does have a niceconcept called const correctness , which provides amodifier on arguments const which disallows anychanges to that argument.

C shares faulty arguments with many otherlanguages. The interaction of C’s pointermechanism with a faulty parameter mechanism,however, makes C considerably worse than mostother languages. In C, pointers are used to simulate

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 51/63

C++?? 48

3rd Edition © Ian Joyner 1996

by-reference arguments with by-value arguments.The programmer must perform tedious bookkeepingby specifying *s and &s for referencing anddereferencing. Distinguishing between by-value andby-reference arguments is not just a syntactic nicety,included in most high level languages, but avaluable compiler technique, as the compiler canautomatically generate the referencing and

dereferencing, without burdening the programmer.Again C adopts operators to provide thefunctionality, rather than a declarative approach,which would centralise decisions and let thecompiler do the rest.

In Java arguments can only be passed by-value(as in C). However, there are no pointers, so passingby-reference cannot be simulated.

Eiffel routine arguments are read-only. Thismeans that they are pass-by-constant which isstronger than pass-by-value, where the argumentsare treated as local variables which may be updated,pass-by-constant disallows this.

4.4 void and void *“Passing paths that climb half way into the void” -Close to the Edge, Yes.

Is void* the C equivalent of an oxymoron? Apointer to void suggests some sort of semanticnonsense, a dangling pointer perhaps? Maybe weshould tell the astronomers we have found a black hole! While we can have some fun conjecturingwhat some of the obscure syntax of C suggests, aserious problem is that void* declarations are usedto compromise the purpose of the type system. Aconsistent strongly-typed system does not requiresuch facilities. In object-oriented type systems, theroot class of the inheritance hierarchy provides theequivalent of void.When an entity is assigned to a reference of void* , it looses its type information. When it is as-signed back to a typed reference the programmermust explicitly specify the type information with atype cast. This is error prone and should at leastresult in a run-time check. Without a runtime typecheck, the routines of one class can be mistakenlyapplied to objects of another class, which results inundetected inconsistencies leading to obscurefailures.

As [Stroustrup 94] points out: “having void*unsafe can be considered acceptable becauseeverybody knows - or at least ought to know - thatcasts from void* are inherently tricky.”Interestingly, void* is the exact opposite of void , so yes this is a programming oxymoron.Void means no object of any type; that is the emptyset. Void* on the other hand means any object of any type; that is all objects of the all encompassingset, or the universal set of all objects that can existin a system. So void and void* representcomplementary sets.

Eiffel and Java both provide a class that is at theroot of the inheritance tree. In Java it is Object ,

and in Eiffel it is ANY . Any object can be assignedto a reference of these types. In C++ this is providedby void*, but void* is not at the root of theinheritance tree, hence its type unsafeness.

Eiffel also defines the type NONE at the bottomof the inheritance tree, which is a class to which noobjects belong. NONE is the complement of ANY and vice versa. Type NONE has the single value

Void , which signifies no object. Void is theequivalent of 0 (meaning NULL) in C++. Thismeans that Eiffel’s type system is more consistent,as ANY and NONE reside within the type hierarchyat the top and bottom respectively. However, voidand void* do not fit into the type hierarchy inC++.

4.5 void fn ()The default return type of a function is int . Atypeless routine returning nothing should be thedefault, but this must be specified by void .Syntactically no <type> suggests nothing to return.This is an example of where C’s syntax is not wellmatched to the concepts and semantics. Also a typedfunction can be invoked independently of anexpression, which is a shorthand way of discardingthe returned value, but compromises type safety.Using a typed function as a void should result in atype error.

In fact there should be no such thing as a voidfunction. A void function is a procedure. Proceduresand functions should be distinguished. Thisdistinction belongs to the problem ‘what’ domain. Aprocedure is a routine that changes the state of itsobject, but returns no value. A function should, ingeneral, not cause any change to the state of anobject, but just return some result dependent upon

the objects state. Mathematically, a function is anentity that returns a value of a given type.Procedures are untyped, and do not return a value,so it is incorrect to regard procedures as functions.Functions have more in common with variables thanprocedures. Procedures may have side effects,functions should not cause side effects. Thesedistinctions are useful when consideringconcurrency.

[Stroustrup 94] also voices the opinion thatdefault int is bad. He had tried to make the typespecifier explicit, but was forced to withdraw byusers: “I backed out the change. I don’t think I had achoice. Allowing that implicit int is the source of

many of the annoying problems with C++ grammartoday. Note the pressure came from users, notmanagement or arm-chair language experts. Finally,ten years later, the C++ ANSI/ISO standardcommittee has decided to deprecate implicit int.”

One improvement in Java is that the result typeof the method is not optional. That is you don’t getint by default. Otherwise, Java does not clean upmost of the deficiencies of C. In order to specify aprocedure rather than function, Java still requires thevoid specifier. Java does discard the C termfunction (which was wrongly used anyway), but

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 52/63

C++?? 49

3rd Edition © Ian Joyner 1996

makes the situation no better by calling bothprocedures and functions methods . Thus there is noclear distinction between procedure and function.Java also allows you to ignore returned values.

Eiffel uses the term routine for called units of code and distinguishes that there are two kinds of routine, procedures and functions . It isrecommended practice that only procedures change

object state, and functions do not. Functions alwaysreturn a value. That is they follow the mathematicaldefinition of function that takes a value of one type(the type may be compound, hence multiplearguments), and maps it to a value of another type.

4.6 fn ()We have already seen that C functions are a poorcousin of mathematical functions in the section oninlines. C functions expose implementation detail;that is, whether an entity is implemented as aconstant, variable or value returning routine. Cfunctions are different to the mathematical conceptof a function. C functions are really parameterisedinvokable code, which other languages callprocedures, subroutines, etc. Java calls themmethods . Data can be accessed functionally in themathematical sense, but this is different to insistingthat all data is accessed through a C function.Functional access to data really means that data canonly be retrieved, not assigned to.

Empty parentheses represent the function calloperator in C. Even though ‘()’ is mathematicallooking, it is semantically equivalent toFORTRAN’s CALL, COBOL’s PERFORM, andJSR in assembler. The design of these operators wasinfluenced by the underlying machine architectures.The function call operator is low level, machine and

execution oriented, and in the ‘how’ domain. Truehigh level languages require no such operator, thecompiler realises from the declaration that the entityreferenced is a function and automatically generatesthe machine call operator.

This is opposite to most Unix shells, whereinvocation operators such as ‘run’ and ‘exec’ are notneeded. One of the nice things about Unix shells isthat the set of in-built commands is extensible. Theability to execute file names as commands extendsthe command repertoire. The shell runs executablesand interprets shell scripts. Unix shells do notdistinguish between inbuilt commands, shell scriptsand executable programs. This is a widely accepted

as an elegant and effective convenience. C’s ()operator introduces the equivalent of a run commandinto the language.

No invocation operator exists in the problemoriented domain of high level languages. This isbecause the semantics of a function is to return avalue of a given type. How this value is computed isunimportant: it could be computed by a routineinvocation; by sending a message across a network;by forking an asynchronous process; or by retrievinga precomputed result from a memory location, ie., avariable.

Languages that have an invocation command oroperator have an unnecessary distinction betweenvalue returning routines and constants and variables.

It is trivial for a compiler to providetransparency of view for constant and variableaccess and function invocation. In ALGOL stylelanguages, the compiler automatically deducesinvocation when it sees a name that was declared as

a routine, rather than a variable. The compilerknows that the identifier refers to a routine becausethe compiler stores much information about anentity. A compiler can check that the programmeruses the entity consistently with the declaration. Acompiler can generate correct code, withoutburdening the programmer with having to use anexplicit invocation operator. This enhancesflexibility and implementation independence.

Variables and functions should beinterchangeable for optimisation. ‘()’ is a goodexample of where the operator approach of low levellanguages adversely affects flexibility as opposed tothe declarative approach of high level languages. In

C, it is not possible to change a function to avariable without removing all the ‘()’, or a variableto a function without adding ‘()’ to all theinvocations. This might be spread over many files,and the programmer might not bother withoptimisation to avoid the tedium of the task. So the() operator reduces flexibility. The () operator isanother bookkeeping task imposed on the Cprogrammer. The C++ recommended style is tocode superfluous accessor functions to blur thedistinction. Pure functional languages such as SMLremove the variable/function distinction altogether,by not having variables at all.

Java has made no improvement here. The visible

implementation difference between variables andfunctions remains. Eiffel removes this distinction asconstants and variables are accessed functionally. Aprogrammer can flexibly change a variable to afunction in a class interface and vice versa foroptimisation or extension, without the need for allclients to change their code. Thus even thoughchanges have been made, the class interface remainsunchanged.

C also has pointers to functions. Functionpointers are analogous to the call by name facility inALGOL, and this was recognised as having pitfalls.Consistent application of the object-orientedparadigm avoids the need for function pointers. A

common use of function pointers is to explicitly setup jump tables. Jump tables are the mechanismbehind virtual functions. The design of a programcan take advantage of this fact, without resorting toexplicit jump tables. Another use is to jump to afunction in a table that is indexed by an inputcharacter. A switch statement can cater for thismechanism that makes what is meant explicit, whilekeeping underlying mechanisms (and possiblyoptimisations) transparent. C++ allows functionpointers to member functions to be stored in tables(via the .* and ->* operators).

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 53/63

C++?? 50

3rd Edition © Ian Joyner 1996

4.7 fn (void)In C f() means the function f can take any numberof arguments of any type without type check. ANSIC has adopted f(void) to mean a function thatreally has no arguments. C++ sensibly differs fromthis in that f() now means a function that has noarguments [Stroustrup 94].

4.8 Metadata in StringsThe implementation of strings in C mixes metadatawith data. Metadata is information about an object,but is not part of the data itself. Examples of metadata are addresses, size and type information.Such metadata is often referred to as datadescriptors, and can be kept independently of thedata, with the advantage that the programmer cannotmistakenly corrupt the metadata.

In C strings, metadata about where stringsterminate is stored in the string data as a terminatingnull byte. This means that the distinction betweendata and metadata is lost. The value chosen as theterminator cannot occur in the data itself. Sinceinserting a null is often the responsibility of theprogrammer, not the run-time environment, there isthe potential for more undetected inconsistenciesresulting in obscure failures.

A common alternative is to store a length byte ina fixed location preceding the string as Pascal does.The advantage is that the length of a string is easilyobtained, without having to count the number of elements up to the terminating null. Anotheradvantage is that 0 is a valid value in a string. Thisimplementation is hidden from the programmer andother methods could be used without theprogrammers having to change the program. C’snull terminator makes the implementation visible tothe programmer.Java’s strings are first class objects. You can’tdetermine the length of a string by scanning for anull. You use the string.length method (function).Eiffel’s strings are also first class objects.

4.9 ++, --The increment and decrement operators are oftenused as an example that C was designed as a highlevel assembler for DEC PDP machines. Theseoperators provide a shorthand convenience, but areunnecessary. There are no less than four ways toperform the same thing -

a = a + 1a += 1a++++a

For full generality, only the first form is required;the last two forms a++ and ++a are the postfix andprefix forms, which can be used in the context of another expression. Thus several updates can beperformed in one expression. This is a verypowerful and convenient feature, but introduces sideeffects into an expression that sometimes have

surprising effects, and can lead to program errors.The following example is given on p.46 of the C++ARM -

i = v[i++]; // the value of ‘i’ is// undefined

The ARM points out that compilers should detectsuch cases, but the exact interpretation appears to beleft to the implementation, which contributes to non-portability. If this can’t be defined for a sequentialprocessor, then it is even worse for a concurrentenvironment.

The shorthand += and -= are more powerful asvalues other than 1 can increment the variable. It hasbeen suggested that there should also be &&= and||= operators.

If it is believed that a multiplicity of operators isrequired to produce more optimal code, then itshould be pointed out that code generators,especially for expressions, can produce the best codefor a target architecture. A plethora of operatorscomplicates the task of an optimiser. A compiler canoptimise well beyond what a programmer can do.An optimising compiler will analyse thesurrounding code, and if an entity is used severaltimes in a local scope, it will keep the value of thatentity handy locally at the top of a stack, or in aregister, rather than retrieve it from slow mainmemory several times. The nature of suchoptimisations depends on the machines architecture,which a programmer should not need to be aware of.Open systems demands that programs can be portedamongst diverse architectures and environments,very different to the original machine, and not onlyrun, but run efficiently. Optimisers work best withsimple, well defined languages.

In fact constructs such as:while (*s1++ = *s2++);

might look optimal to C programmers, but are theantithesis of efficiency. Such constructs precludecompiler optimisation for processors with specificstring handling instructions. A simple assignment isbetter for strings, as it will allow the compiler togenerate optimal code for different target platforms.If the target processor does not have stringinstructions, then the compiler should be responsiblefor generating the above loop code, rather thanrequiring the programmer to write such low levelconstructs. The above loop construct for string

copying is contrary to safety, as there is no check that the destination does not overflow, again anundetected inconsistency which could lead toobscure failures. The above code also makes explicitthe underlying C implementation of strings, that arenull terminated. Such examples show why C cannotbe regarded as a high level language, but rather as ahigh level assembler.

Memory update is a problematic, but necessarypart of programming. A language should provide itin a consistent and expected way. Many languagesrecognise that memory update is problematic, and

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 54/63

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 55/63

C++?? 52

3rd Edition © Ian Joyner 1996

concept is represented in the source text.” Nowonder people are confused, and there is muchheated debate.

In Java null is a reserved word. Eiffel usesVoid , the single value of type NONE , to indicate noobject is referenced.

4.12 Case SensitivityIt is good to adopt typographic conventions fornames, which make a program more readable, butshould not affect semantics. Distinguishing betweenupper and lower case in names can cause confusion,which leads to errors and systems that are difficultto maintain and modify. Case distinction is based onthe implementation paradigm of how charactercodes work. Why do we have names? To giveentities identity, and aid our memory of thatidentity. Philosophically, case distinction is contraryto the fundamental purpose of names, whichintroduces another form of overloading, thedisambiguating mechanism being the underlyingcharacter codes.

Case distinction makes names harder toremember so is contrary to the purpose of a memoryaid. Remembering command mnemonics or filenames is difficult enough, let alone exactly the lettercase. Your brain remembers the sound fred , not thecharacters used in spelling. In a case sensitivesystem, you must remember the letter case, whetherit was fred, Fred or fREd, etc., greatly complicatingthe memory process.

Names are easier to remember than addresses. If we did not have names, we would have to retrievefiles by addresses, access all machines on theInternet by their TCP address instead of host name,or call people by their social security number.

Case distinction in interactive systems is a pooruser interface, being clumsy to continually use theshift key, which slows typing. Case sensitivity isone of the worst features of the Unix interface.

Consider the paradigm of letters and words.Words are spelt by assembling letters in order.There are 26 distinct letters. With the addition of digits 0 to 9, and the underscore character, we havea complete lexical definition for identifiers. Letterscan be written in a number of styles. They can bebold, italic, upper or lower case. Such typographicrepresentations, however, do not change themeaning of a word. Thus if we write ALGOL,Algol, algol, Algol or Algol (or maybe a star), werecognise the word to represent a computerlanguage. The case of the letters or type style doesnot change the semantics.

Case distinction is based on the low levelparadigm of character codes such as ASCII usedinternally in the computer. This weakens thepurpose of using names to replace addresses, asnames are reduced to a string of character codes.

Case distinction also contributes to errors,introducing ambiguity, which as has already beenmentioned, weakens the purpose of names, as

identity is lost. As every programmer will haveexperienced, one character errors are moredifficult to find than one would think. Forexample, if an identifier is declared Fred, anotherone can be declared fred, which are easily mistypedand confused.

We are generally poor proof-readers. Thepsychological reason for this is that the the brain

tends to straighten out errors for our perceptionautomatically. The human brain is an excellentinstrument for working out what was intended, evenin the presence of radical error. (This makes us goodat difficult tasks like speech recognition.)Programmers must use their powers of concentrationto override the natural tendency of the brain.

Case distinction adds cognitive difficulty. Goodlanguage design takes into account suchpsychological considerations in these small butimportant details, being designed towards the wayhumans work, not computers. Such considerationsof cognitive science make a big difference to theeffectiveness of people, but do not have any impact

at all on the efficiency of code generated for thecomputer. What is more important, people orcomputers? With C the answer is often computers,as case distinction saves compiler processor cycles.

Case distinction provides a form of nameoverloading which is a double-edged sword as itleads to ambiguity, confusion and error. Nameoverloading, as has been suggested in the section onname overloading, should only be provided incontrolled and expected ways, where overloadingprovides a useful function such as moduleindependence or polymorphism. Where a name isoverloaded in the same scope the compiler shouldreport an error.

Another example of name overloading error is:class obj{

int Entry;

void set_entry (int entry){

entry = Entry;}

}

If you have not spotted the error in the aboveexample, what was it supposed to mean?

A common practice in C is to representconstants in upper case. This is actually badpractice, as a calling programmer should invoke aconstant as a function that returns a value. Thecalling programmer does not need to know whethera class has implemented a feature as a constant,variable or value returning routine. This means thatthe class is free to change the implementation of thefeature later, without having to bother allprogrammers to change the case of all occurrencesof the identifier in order to follow some style rule.

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 56/63

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 57/63

C++?? 54

3rd Edition © Ian Joyner 1996

fn (a, b+c, d, e,);

It is often argued that the semicolon as separatorleads to irregularities. C’s handling of the grammarof semicolons, however, leads to an irregularity inif/else’s:

if (condition)statement1; /* Semicolon

required */else

statement2;

if (condition){

statement1;} /* Semicolon must be omitted */else

statement2;

This is an irregularity, as a parser will reduce bothof the above to the grammatical form:

“if” <condition> <statement>“else” <statement>

In fact why do conditions in C if and whilestatements have to have parentheses around them?Why also must a semicolon follow the closing braceof a class, but must not follow the closing brace of afunction?

Java being C based retains the semicolon asterminator. Eiffel views the semicolon as aseparator, but has one advantage: semicolons areoptional. The semicolon can be used to visuallyemphasise the separation between two commands,for example, where two commands are placed onone line.

4.16 BooleansA serious omission from C was the boolean type.Booleans are fundamental to programming asconditions in if ..then and loop constructs. C++ alsohas no built in boolean. It is interesting to see longInternet discussions on how booleans should bebuilt, and how to represent the values, true and false.Using 0 to mean false, but any other value to meantrue is unsatisfactory.

Java includes the basic type boolean, and so hasrectified this situation. To accomplish C-styleconversions you can use the expressions:

b = (i != 0);i = (b)?1:0;

Eiffel takes a slightly different approach. As alanguage, Eiffel provides the mechanisms forbuilding types. It has no assumptions aboutparticular types built into the language. Types like

BOOLEAN are defined as classes in the EiffelKernel Library, as are other basic types such as

INTEGER , REAL, STRING , ARRAY , etc. This viewis very similar to Smalltalk. These types are notbuilt into the language , but they are usually built

into an Eiffel compiler so that there is no run-timeperformance penalty. This illustrates Eiffel’sphilosophy of keeping the language as small aspossible, and as open as possible, so thatprogrammers can build their own powerful types.

Recently the ANSI/ISO C++ committee hasaccepted bool as a distinct integral type. Before thedefinition of a boolean type in C/C++ could be any

number of definitions which had slightly differentsemantics. If you were combining libraries that usedthese slightly different definitions, life could bedifficult. This is probably a fundamental reason whylibraries have not been as successful in C++ as theyshould be in an OO environment. Not all compilerimplementations have implemented bool yet, soyou can expect it to be years before this mess iscleaned up.

4.17 CommentsThe following example comes from [GWS 94].

main ()

{int *i, *j;int k;

k = *i/*j;}

As they point out: what a good charactercombination ‘/*’ was for delimiting comments.

4.18 Cpaghe++iThere are three kinds of spaghetti that occur inprograms: gotos, globals, and pointers.

4.18 .1 Cpaghe++i GotosMost people know about spaghetti code that ispresent in programs which use gotos in anundisciplined fashion. As Donald Knuth has pointedout it is entirely possible to produce well structuredprograms with gotos. The well tempered gotoemulates high level structured statements such asconditionals, loops, switch or case statements inhigher level languages.

Where a language provides the correct controlstructures, and the programmer programs into thatparadigm, gotos are not needed. The reverseargument could also be made: if gotos cover all usesof high level control structures and even more, why

have the high level control structures at all; why not just use gotos? The problem with gotos is that theyare too powerful. They are too powerful in the sameway assembler language is too powerful.

You can do everything with assembler or gotos,but it takes more work, and the result is often lessthan structured, difficult to understand andunmaintainable. The more work you do, the lessefficient you are. It is not working harder that makesyou more efficient, it is working smarter. I’m a greatfan of laziness!

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 58/63

C++?? 55

3rd Edition © Ian Joyner 1996

Consider what you must do to construct a loopwith gotos: you must declare a label, then place thelabel and the goto somewhere; you also have tothink about identifiers for labels that are non-ambiguous. For label identifiers, some languagesuse names, others numbers. With a high level loopconstruct, labels are implicit, meaning theprogrammer does not have this extra bookkeeping

overhead. Then making changes becomes a lot moredifficult, as you must create new labels, move themaround, and delete others.

One legitimate use for gotos is to avoid overlycomplex nesting. Complex nesting usually occurswhere there are many checks that result in multiplynesting if...thens, which often arise because of errorchecking. Proponents of gotos legitimately defendthem for this situation. However, where the controlstructures are right, even this use of gotos is notneeded.

Both Java and Eiffel abandon gotos. Javaprovides an extension to control structures whichallows control structures to be named, and multi-

level break and continue statements can be used to jump to an outer level conditional or loop.In Eiffel the philosophy is to program in

sufficiently small atomic routines, so that multi-level control structures are avoided. Thus Eiffel’ssolution to the nesting problem is integrated with itsroutine mechanism and the way programmers areexpected to use routines. In object-orientedprogramming, it is good practice to keep routinessmall, with only one operation in a routine, as thisenhances the possibility of reuse. Someprogrammers will object to small routines, as thereis an overhead to routine calls, particularly inregister based machines, where environments and

registers must be saved. However, an Eiffelcompiler will automatically inline small, non-polymorphic routines.

The high level language concept to remove theneed for gotos altogether for error checking isexception handling. In this mechanism, the errorcondition triggers an exception. When an exceptionis raised, a search for its handler occurs. This searchprogresses down the run-time stack until anembedded exception handler is found. In Eiffel,exception handlers are specified in rescue clauses.Note that in an environment where exceptions caninterrupt the flow of the code, garbage collection iseven more important, as in a system with manual

memory management, it is even more difficult todetermine where to clean up, and which objects todispose.

If exception raising and handling soundsexpensive, then it should be realised that it oftenworks out cheaper. Most of the time, the code runsnormally, an exception being raised is the exception .Only then is the stack search for the handlerperformed. The mechanism actually works outcheaper in many cases. Consider divide by zero. Inmost systems, this exception is detected by theprocessor. If you don’t have exception handling, you

must test that the divisor is not zero before a divideoperation. With exception handling, you assume thatthe division will work in most cases, and so do nothave to test. If the divisor is zero, you simply cleanup in the exception handler. Only if there is noexception handler does the software fail.

The bottom line is that with the common highlevel language constructs of if ..then , loops, cases,

you can avoid most uses of goto. Add a high levelconstruct for exception handling, and you can avoidgotos altogether.

4.18 .2 Cpaghe++i GlobalsThe second kind of spaghetti is globals. Where twoor more objects access the same set of globals,interdependencies arise between those objects. Thismakes it far more difficult to determine thecorrectness of a program, even more so inconcurrent environments. These interdependenciesshould be viewed as strands of spaghetti wormingtheir way through a system, which are going tomake maintenance, extension, and reuse difficult inthe future.

Globals can be abandoned. Objects are toglobals as control structures are to gotos.

Again Java and Eiffel abandon globals, and thusease the problems of maintenance, extension andreuse. Note that I use the word ease, not solve. Eventhough Java and Eiffel make significantimprovements, there are no silver bullets to solvethe problems involved in programming. Java andEiffel are significant improvements.

4.18 .3 Cpaghe++i PointersThe third kind of spaghetti is pointers. The problemswith pointer based programming are well known.The kind of spaghetti you get worming through thesystem is undisciplined pointers pointing to otherelements, by-passing the whole concept of interfacesand object-orientation. Pointers introducedependencies that would not otherwise be there.Furthermore, this can of worms results in danglingreferences and memory leaks. In order to do awaywith the problems of pointers, garbage collection isnecessary. In order to implement good garbagecollection pointers must be abandoned. C++ iscaught in this Catch-22 .

Neither Eiffel nor Java have pointers. Both havegarbage collection built in from scratch.

While C++ overlays object-oriented conceptsonto C, it is one of its greatest weaknesses thatoverlays OO on top of the spaghetti of a now old,low-level and flawed language. C++ does notenforce the advantages of the OO approach toremove these problems by programming only usingpublished interfaces. The advantages of the OOparadigm are so effectively undermined in C++ as tobe worse than useless. Many C programmers havethus stuck to C, and people like P.J. Plauger havebeen motivated to write papers such as“Programming Language Guessing Games: If C++is the answer, what’s the question?” [Plauger 93].

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 59/63

C++?? 56

3rd Edition © Ian Joyner 1996

5. ConclusionsC++ is complex including too many constructs toovercome problems with itself and C, while lackingsophisticated mechanisms such as garbagecollection, global analysis and automaticoptimisations. C is thought of as being a simplelanguage; but this is doubtful, as it has manyoperators, and a difficult precedence system. C’s

pointer style of programming is low level anddifficult. Overall, C has many traps that lead todifficult to detect errors in software. Now C++ as alanguage is looking like the equivalent of computersof the 1950s, with large knobs, dials and patchpanels; the C++ equivalents being pointers,structures, unions, #defines , etc., all of whichhave no place in a modern OO language, and are notin Java and Eiffel.

Compared to other OO languages, C++ looksmore and more like an anachronism. C++ is nowimpeding the progress of the programmingtechnology.

Object-oriented languages should provide

sophisticated concepts in the simplest possibleframework. In C++ the framework is not simple andthe concepts are obscured. OOP addresses manyissues in order to facilitate the production of complex and sophisticated programs. Many of theseissues are addressed in implicit and subtle ways, butare lost in C++. Subtle errors can be introduced intoC++ software in many ways; the combination of these causes further problems. C++ has devices forpetty convenience, even the ‘++’ itself, whilesacrificing major conveniences, long-termcorrectness and safety, and the convenience of declarative programming, rather than operators. C++forces the programmer to perform many

administrative bookkeeping tasks that a compilershould automate.It can be considered: what application domain is

C++ relevant for? The answer to this is that C++might be used as a better C. But for whatapplications is C relevant? C is relevant for lowlevel Unix style programming, and is not an ideallanguage in view of its low level nature, and flaws.C is not applicable for large project organisation:hence C++’s attempt to improve it. C++, however,has not solved C’s flaws, as I once hoped it would,but painfully magnified them.

Better languages exist for higher level functionssuch as communications and networks, scientific

work, compilers, etc. I envisage that C has a place asa high level assembler that can be used to implementsmall pieces of code, where efficiency is of primeimportance, on suitable platforms. Thus the use of Cwould be limited and well controlled, rather likesmall assembler routines are currently used in somesystems. Indeed the move to C++ should only beconsidered in the case of upgrading a body of Cprograms for backwards compatibility. In the case of new projects alternatives to C and C++ shouldseriously be considered.

A programming language should embody thecollective wisdom of common sense practices thathave been learnt over many years, by common andpainful experience. C++ does not implement muchof this wisdom. [Sakkinen 92] observes that muchof the C++ literature has few references to externalwork or research. It fails to draw on the insights andprogress made by many researchers. This leads me

to believe that C++ is parochial and removed fromthe many advances that will make production of systems easier and more cost effective.

C encourages gurus who spout false wisdom onobscure subjects. Writing programs in C is oftencalled ‘coding’. Coding is writing obscureencryptions that will later have to be decoded, bynone else than a guru! C also encouragesprogramming by guesswork. C programmers oftensolve ‘bugs’ by adding extra ()s, *s and &s, withoutunderstanding the problem, but then ‘test’ thechange to see if it miraculously ‘cures’ the problem.People who attain proficiency at this guesswork, areknown as, well you guessed it, gurus!!

The view that correctness checks are trainingwheels for students, which gurus don’t need must bedispelled. Many disciplines have techniques toensure correctness. For example, the metronome inmusic is not just for students, but will help anadvanced musician ensure that the tempo of a pieceis correct, and since playing with a metronome ismore difficult it will help sharpen the musiciansperformance of the piece. The musician does not

just view the metronome as an aid for beginners, oras something that restricts him to a set beat, but as atool that helps produce a polished and professionalperformance. C should not be seen as a language towhich you graduate after you have learnt to programin languages with safety checks. In fact changing toC or C++ is a great step backwards. Languages withconsistency and semantic checks are essential aids tothe production of professional software.

A programming language cannot be seriouslyviewed as some authoritarian that stops us doingwhat we want or need to do. This view is still quiteprevalent about languages with type safety andconsistency checks.

This paper has shown many cases where C++uses old C mechanisms to provide things that canand should be expressed consistently within theobject-oriented paradigm. For example type casting.The move to pure object-oriented languages will

facilitate more consistent programming and avoidmany typical errors that occur in softwareproduction. C++ also makes distinctions that belongin the ‘how’ implementation domain. For example,‘.’ vs ‘->’, and variables vs functions. Thesedistinctions make bookkeeping work forprogrammers, which a compiler should handle. Butthen C++ fails to make distinctions that belong inthe ‘what’ problem domain. For example,procedures vs functions. Making distinctions in the‘how’ domain adds inconvenience to the language.Failing to make distinctions in the ‘what’ domain

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 60/63

C++?? 57

3rd Edition © Ian Joyner 1996

limits the expressiveness of the language. Theamount of change required in C++ to address theissues raised in this paper is seen as largelyinsurmountable, and Sun agrees with this.

A programming language is just a tool, in thesame way that an axe is a tool. If the axe is bluntwhen chopping down a tree, then procedures,processes and methodologies could be invented to

make it as effective as possible; but that leaves thereal problem unsolved: that the axe that does the realwork is blunt. So it is with programming languages.To develop a system, it must be implemented, and aprogramming language is the tool to do the realwork. If the language is blunt, then procedures,processes and methodologies might alleviate the sit-uation, but they do not solve the problem. Once theaxe is sharpened, then real progress is made, and theprocedures, processes and methodologies mightbecome more effective, although the need for manyof them will disappear. A good axeman will havegood axe wielding technique, but given a choice of axes will choose the sharpest implement. A poor

axeman could be ineffective with even a sharp axe,but the axe maker will still strive to produce thesharpest axe for the good axeman. The argumentthat poor programmers will produce bad programsin any language so we shouldn’t bother with betterlanguages is fallacious.

As mentioned in the introduction, both sides of the analysis/design vs implementation debate needto compromise in order to bridge the semantic gap.The perpetuation of low level languages such as Cinto OOP is proof that the implementationcommunity has not compromised, or sharpened itsaxe to bridge this costly gap. On the other hand theanalysis/design community must realise that what

they do is part of the general practice of programming.It has been four years since the 2nd edition of

this critique. The criticisms are still valid, but nowmany people have had first hand experience of beingburnt by the OO hype and trying to implementsystems in C++.

The work on languages such as Java and Eiffelhas vindicated the criticisms previously made in thecritique. [Stroustrup 94] lists as current C++problems many of the criticisms I have also made inthe critique. Java has recognised many shortcomingsin C++ and rectified them. Many of the problemsthat Java fixes are the same problems as addressed

in the original critique.Eiffel serves as another example of betterlanguage design than C++. It has none of theproblems of C++. In Java there still remain a fewdeficiencies, but it is a major advance.

Since the last edition of the critique, manypeople have asked what do I recommend. Whatshould people choose then? Certainly Eiffel is thebest out of these three languages. If you are doinglarge scale system software and applicationdevelopment, then the choice is Eiffel, althoughEiffel is also simple and elegant enough for small

applications development. Eiffel is a language forthe serious software engineer who wants to get onwith the job, not be bogged down in syntactic andmachine-oriented obscurities, weird ‘bugs’ andendless maintenance cycles to get things right.

Java is still an unproven entity for large projects,and the byte code is interpreted. Eiffel and C++ areroughly equivalent in performance. Interpreted Java

will be around 10 times slower. But Java byte codescould be compiled into native code.For small applets and other Internet loaded

applications, Java is a good choice. Some peoplehave predicated that Java will sweep all away, andthat even Eiffel will die because of this. I cannot seethis, as Eiffel and Java are really significantlydifferent tools. Java has still to be tested in the largescale Eiffel league.

I have not yet mentioned languages such asBETA, Ada 95 or Smalltalk. BETA is still really inacademia. It might make a stronger presence in themarket place in the coming years. If not BETAmight have the same profound influence as Simula.

It is certainly something to be watched. Ada 95 iscertainly aimed at serious software engineering.Smalltalk is already firmly in the market place,

and there are a significant number of systems that itis used for. Smalltalk is still a language for seriousconsideration. The biggest question here is do youwant the development speed and flexibility of adynamically typed system as opposed to therobustness and run-time speed of a statically typedsystem? Having answered these questions foryourself the choice between Smalltalk and Eiffelshould be easier.

The most important aspect of C++ that theindustry must realise is that the definition of C++ is

unstable. As the X3J16 committee work on C++,more problems are uncovered. It will be years beforea stable standard is reached, and probably years afterthat before compiler vendors are compliant with thestandard.

Today’s C++ programs will be tomorrow’sunmaintainable legacy code. As [GWS 94] says of C++: “The seeds of software disasters for decades tocome have already been planted and well fertilised.”They compare C++ to COBOL in terms of unmaintainable legacy code which we have now inCOBOL’s case, and we will have in the future forC++.

Perhaps the most important realisation I had

while developing this critique is that high levellanguages are more important to programming thanobject-orientation. That is, languages which have theattribute that they remove the burden of bookkeeping from the programmer to enhancemaintainability and flexibility are more significantthan languages which just add object-orientedfeatures. While C++ adds object-orientation to C, itfails in the more important attribute of being highlevel. This greatly diminishes any benefits of theobject-oriented paradigm.

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 61/63

C++?? 58

3rd Edition © Ian Joyner 1996

In a nutshell, an object-oriented language thatlacks the qualities of a high level language entirelymisses the point of why we have progressed frommachine coding to symbolic assembler and beyond.Without the essential high level qualities, OO isnothing but hype. Eiffel shows that it is important tobe high level as well as OO, and I hope that thelesson to be learned by any programming paradigm,

not just OO, is that the fundamental is to make thetask of programming (that is system development asa whole) easier by the removal of the burden of bookkeeping.

C++ adds object-orientation to a low levellanguage, so you still have all the bookkeepingburden of C. Java improves this situation byremoving many of the low level features that have aknown bad track record. Eiffel provides a true highlevel base for object-oriented programming.

The concluding advice of this critique is clear.Be wary of C++. Seriously consider the alternativelanguages.

Bjarne Stroustrup writes “My hope is that it will

help C++ become accepted into areas that C failedto penetrate, and thus support programmers whohave not been represented in the C and C++culture.” [Stroustrup 94] 6.5.3.1. My hope is that theindustry establishes a professional softwareengineering culture, not a programming languageculture based on seriously flawed and arcanelanguages. The software engineering culture is notwell represented in C++.

Ian Joyner October 1996

6. BibliographyC++ ARM ELLIS and STROUSTRUP The annotated C++ Reference Manual , AT&T 1990.[Adams 96] SCOTT ADAMS The Dilbert Principle ,Harper Collins 1996.[Aho 92] AHO and ULLMAN Foundations of Computer Science , Computer Science Press 1992.[Brooks 95] FREDERICK P. B ROOKS The Mythical Man-Month , 20th Anniversary Edition, AddisonWesley.[Bruce 96] KIM B. BRUCE Progress inProgramming Languages , in ACM ComputingSurveys, Vol. 28, No. 1, March 1996.[Capretz 87] PIERRE J. CAPRETZ French in Action, A Beginning Course in Language and Culture , YaleUniversity Press.[Cline] MARSHALL CLINE C++ Frequently Asked Questions , comp.lang.c++ newsgroup.[DDH 72] DAHL, DIJKSTRA , HOARE Structured Programming.

[Deming 82] W. E DWARDS DEMING Out of theCrisis , Cambridge University Press 1982.

[Dijkstra 76] E.W. D IJKSTRA A Discipline of Programming , Prentice Hall 1976.[DM&L 87] TOM DE MARCO and T IMOTHY LISTER ,Peopleware: Productive Projects and Teams , DorsetHouse 1987.[Ege 96] STUART HIRSHFIELD and RAIMUND K.EGE Object-Oriented Programming , In ACM

Computing Surveys, Vol. 28, No. 1, March 1996.[Ellemtel 92] Programming in C++: Rules and Recommendations , Ellemtel TelecommunicationSystems Laboratories, Sweden.[Flan 96] DAVID FLANAGAN Java in a Nutshell ,O’Reilly & Associates 1996.[GWS 94] GARFINKEL , WEISS , STRASSMANN TheUnix-Haters Handbook , IDG books 1994.[Hext 90] J.B. HEXT Programming Structures: Machines and Programs. Volume I , Prentice Hall of Australia 1990.[Ince 92] D.C.INCE Arrays and Pointers Considered Harmful , ACM SigPlan Notices, January 1992.[Kilov and Ross 94] HAIM KILOV and JAMES ROSS, Information Modelling: An Object-oriented Approach , Prentice Hall 1994.[L&S 95] WILLIAM J. LATZKO and DAVID M.SAUNDERS , Four days with Dr. Deming: A strategy

for modern methods of management , AddisonWesley 1995.[Madsen 93] MADSEN , MØLLER -PEDERSEN ,NYGAARD , Object-Oriented Programming in the

BETA Programming Language , Addison Wesley1993.[Meyer 88] BERTRAND MEYER Object-oriented Software Construction , Prentice Hall 1988. (2ndedition soon to appear.)[Meyer 92] BERTRAND MEYER Eiffel: The Language , Prentice Hall 1992.[Meyer 94] BERTRAND MEYER Reusable Software:The Base Object-oriented Component Libraries ,Prentice Hall 1994.[Meyer 95] BERTRAND MEYER Object Success ,Prentice Hall 1995.[Meyer 96a] BERTRAND MEYER A Taxonomy of

Inheritance , IEEE Computer vol 29 No 5 May 1996.[Meyer 96b] BERTRAND MEYER Using InheritanceWell , Chapter 25 of forthcoming Object-oriented Software Construction , 2nd edition Prentice Hall.Draft available on internet athttp://www.eiffel.com/doc/manuals/technology/ oosc/inheritance-design/ [Meyer 96c] BERTRAND MEYER Concurrency, Distribution and the Internet , Chapter 28 of forthcoming Object-oriented Software Construction ,

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 62/63

C++?? 59

3rd Edition © Ian Joyner 1996

2nd edition Prentice Hall. Draft available on internetat:http://www.eiffel.com/doc/manuals/technology/concurrency/CONCURRENCY.html[Mody 91] R.P.M ODY C in Education and Software Engineering , ACM SIGCSE Bulletin Vol.23 No. 3September 1991.

[Morgan 90] CARROLL MORGAN Programming from Specifications , Prentice Hall 1990.[P&S 94] JENS PALSBERG and MICHAEL I.SCHWARTZBACH Object-oriented Type Systems ,Wiley 1994.[Plauger 93] P.J. P LAUGER Programming LanguageGuessing Games: If C++ is the Answer, what’s thequestion? , Dr Dobb’s Journal, October 1993.[Reade 89] CHRIS READE Elements of FunctionalProgramming , Addison-Wesley, 1989.[RBPEL91] RUMBAUGH , BLAHA , PREMERLANI ,EDDY, LORENSEN Object-Oriented modelling and

Design , Prentice-Hall, 1991.[Sakkinen 92] MARKKU SAKKINEN Inheritance and Other Main Principles of C++ and Other Object-oriented Languages , University of Jyväskylä, 1992.(Also published as selected papers in ECOOP ‘88,Computing Systems Vol. 5 No. 1, and StructuredProgramming Vol. 13 (1992).)[Shaw 96] MARY SHAW and DAVID GARLANSoftware Architecture: Perspectives on an emergingdiscipline , Prentice Hall 1996.[SJE 91] SAAKE , JUNGCLAUS , EHRICH Object-Oriented Specification and Stepwise Refinement , in

IFIP Workshop on Open Distributed ProcessingBerlin, 1991.[Stroustrup 94] BJARNE STROUSTRUP The Designand Evolution of C++ , Addison Wesley 1994.[Sun 95] The Java Language Environment: A WhitePaper , Sun 1995. (http://java.sun.com)[Sun 96] The Java Language Specification , Sun1996. See WEB address.[Weg 91] PETER WEGNER Concepts and Paradigmsof Object-Oriented Programming , ACM SIGPLANOOPS Messenger Volume 1 no. 1 August 1990.

[Wiener 95] RICHARD

WIENER

Software Development Using Eiffel: There can be life other than C++ , Prentice Hall 1995.[X3J16 92] Members of the X3J16 working groupon extensions How to write a C++ Language

Extension Proposal for ANSI-X3j16/ISO-WG21 ,ACM SIGPLAN Notices Vol. 27 No. 6 June 1992.[Yoshida 92] KOICHIRO YOSHIDA Title and book inJapanese.

7. Webliography

Ada 95 : Home page

http://lglwww.epfl.ch/Ada/ Ada 95 Guide for C/C++ Programmers

http://lglwww.epfl.ch/Ada/Ammo/ Cplpl2Ada.htmlContrast to C++ by Edmond Schonberg

http://www.csci.unt.edu/faculty/ryan/languages/ada/ 9x-cplus.txtBeta :http://www.daimi.aau.dk/~beta/ C++:

FAQ

http://www.cs.bham.ac.uk/~jdm/CPP/cppfaq.html ISO SC22/WG21 standards

ftp://research.att.com/dist/c++std/WPftp://ftp.maths.warwick.ac.uk/pub/c++/std/WPhttp://www.cygnus.com/misc/wp/index.htmlhttp://reality.sgi.com/employees/austern_mti/std-c++/faq.html#B8STL

http://www.cs.rpi.edu/~musser/stl.htmlComments on Critique

http://www.cs.oberlin.edu/students/jbasney/critique/ critique.htmlDilbert :The Dilbert Zone

http://www.unitedmedia.com/comics/dilbert/ Eiffel :

EiffelWorld magazine

http://www.eiffel.com/doc/eiffelworld/ Downline load site for SmallEiffel

ftp://ftp.loria.fr/pub/loria/genielog/SmallEiffel/ Interactive Software Engineering

http://www.eiffel.com/ Dynamic Linking in Eiffel

http://www.eiffel.com/doc/manuals/dle/book Vendor independent home page

http://arachnid.cs.cf.ac.uk/CLE/ Books on Eiffel

8/14/2019 cppcv3

http://slidepdf.com/reader/full/cppcv3 63/63

C++?? 60

http://www.eiffel.com/doc/documentation.htmlSIG computer and Visual Eiffel

http://www.sigco.com/ Tower Technology

http://www.twr.com/ Eiffel locater page

http://www.progsoc.uts.edu.au/~geldridg/stop-press.htmlJava :

Main page

http://java.sun.com/ Demonstration Applets

http://www.gamelan.com/index.shtml Java Language Specification

http://java.sun.com/doc/language_specification/

index.htmlOberon :The Oberon Reference Site

http://www.math.tau.ac.il/~laden/oberon/ Sakkinen, Markku :http://www.cs.jyu.fi/~sakkinen/

References to other papers on C++ and other topicsby Dr. Sakkinen.

X3J16 C++ ISO standardisation:

http://www.x3.org/tc_home/x3j16.html