B. Stroustrup - The C++ Programming Language (794319), страница 19
Текст из файла (страница 19)
The majority of C++ constructs arededicated to the design and implementation of elegant and efficient abstractions (e.g., user-definedtypes and algorithms using them). One effect of this modularity and abstraction (in particular, theuse of libraries) is that the point where a run-time error can be detected is separated from the pointwhere it can be handled. As programs grow, and especially when libraries are used extensively,standards for handling errors become important.2.4.3.1 ExceptionsConsider again the Vector example. What ought to be done when we try to access an element thatis out of range for the vector from §2.3.2?• The writer of Vector doesn’t know what the user would like to have done in this case (thewriter of Vector typically doesn’t even know in which program the vector will be running).• The user of Vector cannot consistently detect the problem (if the user could, the out-of-rangeaccess wouldn’t happen in the first place).The solution is for the Vector implementer to detect the attempted out-of-range access and then tellthe user about it.
The user can then take appropriate action. For example, Vector::operator[]() candetect an attempted out-of-range access and throw an out_of_range exception:double& Vector::operator[](int i){if (i<0 || size()<=i) throw out_of_range{"Vector::operator[]"};return elem[i];}The throw transfers control to a handler for exceptions of type out_of_range in some function thatdirectly or indirectly called Vector::operator[]().
To do that, the implementation will unwind thefunction call stack as needed to get back to the context of that caller (§13.5.1). For example:void f(Vector& v){// ...try { // exceptions here are handled by the handler defined belowv[v.size()] = 7; // try to access beyond the end of v}catch (out_of_range) { // oops: out_of_range error// ...
handle range error ...}// ...}We put code for which we are interested in handling exceptions into a try-block. That attemptedassignment to v[v.size()] will fail. Therefore, the catch-clause providing a handler for out_of_rangewill be entered. The out_of_range type is defined in the standard library and is in fact used by somestandard-library container access functions.Use of the exception-handling mechanisms can make error handling simpler, more systematic,and more readable. See Chapter 13 for further discussion, details, and examples.56A Tour of C++: The BasicsChapter 22.4.3.2 InvariantsThe use of exceptions to signal out-of-range access is an example of a function checking its argument and refusing to act because a basic assumption, a precondition, didn’t hold.
Had we formallyspecified Vector’s subscript operator, we would have said something like ‘‘the index must be in the[0:size()) range,’’ and that was in fact what we tested in our operator[](). Whenever we define afunction, we should consider what its preconditions are and if feasible test them (see §12.4, §13.4).However, operator[]() operates on objects of type Vector and nothing it does makes any senseunless the members of Vector have ‘‘reasonable’’ values.
In particular, we did say ‘‘elem points toan array of sz doubles’’ but we only said that in a comment. Such a statement of what is assumedto be true for a class is called a class invariant, or simply an invariant. It is the job of a constructorto establish the invariant for its class (so that the member functions can rely on it) and for the member functions to make sure that the invariant holds when they exit. Unfortunately, our Vector constructor only partially did its job.
It properly initialized the Vector members, but it failed to checkthat the arguments passed to it made sense. Consider:Vector v(−27);This is likely to cause chaos.Here is a more appropriate definition:Vector::Vector(int s){if (s<0) throw length_error{};elem = new double[s];sz = s;}I use the standard-library exception length_error to report a non-positive number of elementsbecause some standard-library operations use that exception to report problems of this kind. Ifoperator new can’t find memory to allocate, it throws a std::bad_alloc. We can now write:void test(){try {Vector v(−27);}catch (std::length_error) {// handle negative size}catch (std::bad_alloc) {// handle memory exhaustion}}You can define your own classes to be used as exceptions and have them carry arbitrary informationfrom a point where an error is detected to a point where it can be handled (§13.5).Often, a function has no way of completing its assigned task after an exception is thrown.Then, ‘‘handling’’ an exception simply means doing some minimal local cleanup and rethrowingthe exception (§13.5.2.1).Section 2.4.3.2Invariants57The notion of invariants is central to the design of classes, and preconditions serve a similar rolein the design of functions.
Invariants• helps us to understand precisely what we want• forces us to be specific; that gives us a better chance of getting our code correct (afterdebugging and testing).The notion of invariants underlies C++’s notions of resource management supported by constructors (§2.3.2) and destructors (§3.2.1.2, §5.2). See also §13.4, §16.3.1, and §17.2.2.4.3.3 Static AssertionsExceptions report errors found at run time. If an error can be found at compile time, it is usuallypreferable to do so. That’s what much of the type system and the facilities for specifying the interfaces to user-defined types are for.
However, we can also perform simple checks on other properties that are known at compile time and report failures as compiler error messages. For example:static_assert(4<=sizeof(int), "integers are too small"); // check integer sizeThis will write integers are too small if 4<=sizeof(int) does not hold, that is, if an int on this systemdoes not have at least 4 bytes.
We call such statements of expectations assertions.The static_assert mechanism can be used for anything that can be expressed in terms of constantexpressions (§2.2.3, §10.4). For example:constexpr double C = 299792.458;// km/svoid f(double speed){const double local_max = 160.0/(60∗60);// 160 km/h == 160.0/(60*60) km/sstatic_assert(speed<C,"can't go that fast");static_assert(local_max<C,"can't go that fast");// error : speed must be a constant// OK// ...}In general, static_assert(A,S) prints S as a compiler error message if A is not true.The most important uses of static_assert come when we make assertions about types used asparameters in generic programming (§5.4.2, §24.3).For runtime-checked assertions, see §13.4.2.5 PostscriptThe topics covered in this chapter roughly correspond to the contents of Part II (Chapters 6–15).Those are the parts of C++ that underlie all programming techniques and styles supported by C++.Experienced C and C++ programmers, please note that this foundation does not closely correspondto the C or C++98 subsets of C++ (that is, C++11).58A Tour of C++: The Basics2.6 Advice[1][2][3]Don’t panic! All will become clear in time; §2.1.You don’t have to know every detail of C++ to write good programs; §1.3.1.Focus on programming techniques, not on language features; §2.1.Chapter 23A Tour of C++: Abstraction MechanismsDon’t Panic!– Douglas Adams•••••IntroductionClassesConcrete Types; Abstract Types; Virtual Functions; Class HierarchiesCopy and MoveCopying Containers; Moving Containers; Resource Management; Suppressing OperationsTemplatesParameterized Types; Function Templates; Function Objects; Variadic Templates; AliasesAdvice3.1 IntroductionThis chapter aims to give you an idea of C++’s support for abstraction and resource managementwithout going into a lot of detail.
It informally presents ways of defining and using new types(user-defined types). In particular, it presents the basic properties, implementation techniques, andlanguage facilities used for concrete classes, abstract classes, and class hierarchies. Templates areintroduced as a mechanism for parameterizing types and algorithms with (other) types and algorithms. Computations on user-defined and built-in types are represented as functions, sometimesgeneralized to template functions and function objects. These are the language facilities supportingthe programming styles known as object-oriented programming and generic programming.
Thenext two chapters follow up by presenting examples of standard-library facilities and their use.The assumption is that you have programmed before. If not, please consider reading a textbook, such as Programming: Principles and Practice Using C++ [Stroustrup,2009], before continuing here. Even if you have programmed before, the language you used or the applications youwrote may be very different from the style of C++ presented here.