B. Stroustrup - The C++ Programming Language (794319), страница 6
Текст из файла (страница 6)
Therefore, a language feature anda fundamental abstraction must be designed not to waste a single byte or a single processorcycle compared to equivalent alternatives. This is known as the zero-overhead principle.These are Draconian principles, but essential in some (but obviously not all) contexts. In particular,the zero-overhead principle repeatedly led C++ to simpler, more elegant, and more powerful facilities than were first envisioned. The STL is an example (§4.1.1, §4.4, §4.5, Chapter 31, Chapter 32,Chapter 33). These principles have been essential in the effort to raise the level of programming.1.2.1 Programming StyleLanguages features exist to provide support for programming styles.
Please don’t look at an individual language feature as a solution, but as one building brick from a varied set which can be combined to express solutions.The general ideals for design and programming can be expressed simply:• Express ideas directly in code.• Express independent ideas independently in code.• Represent relationships among ideas directly in code.• Combine ideas expressed in code freely – where and only where combinations make sense.• Express simple ideas simply.These are ideals shared by many people, but languages designed to support them can differ dramatically. A fundamental reason for that is that a language embodies a set of engineering tradeoffsreflecting differing needs, tastes, and histories of various individuals and communities.
C++’sanswers to the general design challenges were shaped by its origins in systems programming (goingback to C and BCPL [Richards,1980]), its aim to address issues of program complexity throughabstraction (going back to Simula), and its history.Section 1.2.1Programming Style11The C++ language features most directly support four programming styles:• Procedural programming• Data abstraction• Object-oriented programming• Generic programmingHowever, the emphasis is on the support of effective combinations of those. The best (most maintainable, most readable, smallest, fastest, etc.) solution to most nontrivial problems tends to be onethat combines aspects of these styles.As is usual with important terms in the computing world, a wide variety of definitions of theseterms are popular in various parts of the computing industry and academia.
For example, what Irefer to as a ‘‘programming style,’’ others call a ‘‘programming technique’’ or a ‘‘paradigm.’’ I prefer to use ‘‘programming technique’’ for something more limited and language-specific. I feeluncomfortable with the word ‘‘paradigm’’ as pretentious and (from Kuhn’s original definition) having implied claims of exclusivity.My ideal is language facilities that can be used elegantly in combination to support a continuumof programming styles and a wide variety of programming techniques.• Procedural programming: This is programming focused on processing and the design ofsuitable data structures.
It is what C was designed to support (and Algol, and Fortran, aswell as many other languages). C++’s support comes in the form of the built-in types, operators, statements, functions, structs, unions, etc. With minor exceptions, C is a subset ofC++. Compared to C, C++ provides further support for procedural programming in theform of many additional language constructs and a stricter, more flexible, and more supportive type system.• Data abstraction: This is programming focused on the design of interfaces, hiding implementation details in general and representations in particular. C++ supports concrete andabstract classes.
The facilities for defining classes with private implementation details, constructors and destructors, and associated operations directly support this. The notion of anabstract class provides direct support for complete data hiding.• Object-oriented programming: This is programming focused on the design, implementation,and use of class hierarchies. In addition to allowing the definition lattices of classes, C++provides a variety of features for navigating class lattices and for simplifying the definitionof a class out of existing ones. Class hierarchies provide run-time polymorphism (§20.3.2,§21.2) and encapsulation (§20.4, §20.5).• Generic programming: This is programming focused on the design, implementation, and useof general algorithms.
Here, ‘‘general’’ means that an algorithm can be designed to accept awide variety of types as long as they meet the algorithm’s requirements on its arguments.The template is C++’s main support for generic programming. Templates provide (compiletime) parametric polymorphism.Just about anything that increases the flexibility or efficiency of classes improves the support of allof those styles. Thus, C++ could be (and has been) called class oriented.Each of these styles of design and programming has contributed to the synthesis that is C++.Focusing exclusively on one of these styles is a mistake: except for toy examples, doing so leads towasted development effort and suboptimal (inflexible, verbose, poorly performing, unmaintainable,etc.) code.12Notes to the ReaderChapter 1I wince when someone characterizes C++ exclusively through one of these styles (e.g., ‘‘C++ isan object-oriented language’’) or uses a term (e.g., ‘‘hybrid’’ or ‘‘mixed paradigm’’) to imply that amore restrictive language would be preferable.
The former misses the fact that all the styles mentioned have contributed something significant to the synthesis; the latter denies the validity of thesynthesis. The styles mentioned are not distinct alternatives: each contributes techniques to a moreexpressive and effective style of programming, and C++ provides direct language support for theiruse in combination.From its inception, the design of C++ aimed at a synthesis of programming and design styles.Even the earliest published account of C++ [Stroustrup,1982] presents examples that use these different styles in combination and presents language features aimed at supporting such combinations:• Classes support all of the mentioned styles; all rely on the user representing ideas as userdefined types or objects of user-defined types.• Public/private access control supports data abstraction and object-oriented programming bymaking a clear distinction between interface and implementation.• Member functions, constructors, destructors, and user-defined assignment provide a cleanfunctional interface to objects as needed by data abstraction and object-oriented programming.
They also provide a uniform notation as needed for generic programming. Moregeneral overloading had to wait until 1984 and uniform initialization until 2010.• Function declarations provide specific statically checked interfaces to member functions aswell as freestanding functions, so they support all of the mentioned styles. They are necessary for overloading. At the time, C lacked ‘‘function prototypes’’ but Simula had functiondeclarations as well as member functions.• Generic functions and parameterized types (generated from functions and classes usingmacros) support generic programming. Templates had to wait until 1988.• Base and derived classes provide the foundation for object-oriented programming and someforms of data abstraction. Virtual functions had to wait until 1983.• Inlining made the use of these facilities affordable in systems programming and for buildingrun-time and space efficient libraries.These early features are general abstraction mechanisms, rather than support for disjoint programming styles.
Today’s C++ provides much better support for design and programming based onlightweight abstraction, but the aim of elegant and efficient code was there from the very beginning.The developments since 1981 provide much better support for the synthesis of the programmingstyles (‘‘paradigms’’) originally considered and significantly improve their integration.The fundamental object in C++ has identity; that is, it is located in a specific location in memory and can be distinguished from other objects with (potentially) the same value by comparingaddresses.
Expressions denoting such objects are called lvalues (§6.4). However, even from theearliest days of C++’s ancestors [Barron,1963] there have also been objects without identity(objects for which an address cannot be safely stored for later use). In C++11, this notion of rvaluehas been developed into a notion of a value that can be moved around cheaply (§3.3.2, §6.4.1,§7.7.2). Such objects are the basis of techniques that resemble what is found in functional programming (where the notion of objects with identity is viewed with horror).
This nicely complements the techniques and language features (e.g., lambda expressions) developed primarily forgeneric programming. It also solves classical problems related to ‘‘simple abstract data types,’’such as how to elegantly and efficiently return a large matrix from an operation (e.g., a matrix +).Section 1.2.1Programming Style13From the very earliest days, C++ programs and the design of C++ itself have been concernedabout resource management.
The ideal was (and is) for resource management to be• simple (for implementers and especially for users),• general (a resource is anything that has to be acquired from somewhere and later released),• efficient (obey the zero-overhead principle; §1.2),• perfect (no leaks are acceptable), and• statically type-safe.Many important C++ classes, such as the standard library’s vector, string, thread, mutex, unique_ptr,fstream, and regex, are resource handles. Foundation and application libraries beyond the standardprovided many more examples, such as Matrix and Widget.
The initial step in supporting thenotion of resource handles was taken with the provision of constructors and destructors in the veryfirst ‘‘C with Classes’’ draft. This was soon backed with the ability to control copy by definingassignment as well as copy constructors. The introduction of move constructors and move assignments (§3.3) in C++11 completes this line of thinking by allowing cheap movement of potentiallylarge objects from scope to scope (§3.3.2) and to simply control the lifetime of polymorphic orshared objects (§5.2.1).The facilities supporting resource management also benefit abstractions that are not resourcehandles.
Any class that establishes and maintains an invariant relies on a subset of those features.1.2.2 Type CheckingThe connection between the language in which we think/program and the problems and solutionswe can imagine is very close. For this reason, restricting language features with the intent of eliminating programmer errors is, at best, dangerous. A language provides a programmer with a set ofconceptual tools; if these are inadequate for a task, they will be ignored.