B. Stroustrup - The C++ Programming Language (794319), страница 96
Текст из файла (страница 96)
Such nonlocal variables in a translation unit areinitialized in their definition order. If such a variable has no explicit initializer, it is by default initialized to the default for its type (§17.3.3). The default initializer value for built-in types and enumerations is 0. For example:double x = 2;// nonlocal variablesdouble y;double sqx = sqrt(x+y);Here, x and y are initialized before sqx, so sqrt(2) is called.There is no guaranteed order of initialization of global variables in different translation units.Consequently, it is unwise to create order dependencies between initializers of global variables indifferent compilation units.
In addition, it is not possible to catch an exception thrown by the initializer of a global variable (§13.5.2.5). It is generally best to minimize the use of global variablesand in particular to limit the use of global variables requiring complicated initialization.Several techniques exist for enforcing an order of initialization of global variables in differenttranslation units. However, none are both portable and efficient. In particular, dynamically linkedlibraries do not coexist happily with global variables that have complicated dependencies.Often, a function returning a reference is a good alternative to a global variable.
For example:int& use_count(){static int uc = 0;return uc;}A call use_count() now acts as a global variable except that it is initialized at its first use (§7.7). Forexample:void f(){cout << ++use_count(); // read and increment// ...}Like other uses of static, this technique is not thread-safe. The initialization of a local static isthread-safe (§42.3.3). In this case, the initialization is even with a constant expression (§10.4), sothat it is done at link time and not subject to data races (§42.3.3).
However, the ++ can lead to adata race.The initialization of nonlocal (statically allocated) variables is controlled by whatever mechanism an implementation uses to start up a C++ program. This mechanism is guaranteed to workproperly only if main() is executed. Consequently, one should avoid nonlocal variables that requirerun-time initialization in C++ code intended for execution as a fragment of a non-C++ program.Note that variables initialized by constant expressions (§10.4) cannot depend on the value ofobjects from other translation units and do not require run-time initialization.
Such variables aretherefore safe to use in all cases.Section 15.4.2Initialization and Concurrency44315.4.2 Initialization and ConcurrencyConsider:int x = 3;int y = sqrt(++x);What could be the values of x and y? The obvious answer is ‘‘3 and 2!’’ Why? The initialization ofa statically allocated object with a constant expression is done at link time, so x becomes 3. However, y’s initializer is not a constant expression (sqrt() is no constexpr), so y is not initialized untilrun time. However, the order of initialization of statically allocated objects in a single translationunit is well defined: they are initialized in definition order (§15.4.1).
So, y becomes 2.The flaw in this argument is that if multiple threads are used (§5.3.1, §42.2), each will do therun-time initialization. No mutual exclusion is implicitly provided to prevent a data race. Then,sqrt(++x) in one thread may happen before or after the other thread manages to increment x. So, thevalue of y may be sqrt(4) or sqrt(5).To avoid such problems, we should (as usual):• Minimize the use of statically allocated objects and keep their initialization as simple aspossible.• Avoid dependencies on dynamically initialized objects in other translation units (§15.4.1).In addition, to avoid data races in initialization, try these techniques in order:[1] Initialize using constant expressions (note that built-in types without initializers are initialized to zero and that standard containers and strings are initialized to empty by linktime initialization).[2] Initialize using expressions without side effects.[3] Initialize in a known single-threaded ‘‘startup phase’’ of computation.[4] Use some form of mutual exclusion (§5.3.4, §42.3).15.4.3 Program TerminationA program can terminate in several ways:[1] By returning from main()[2] By calling exit()[3] By calling abort()[4] By throwing an uncaught exception[5] By violating noexcept[6] By calling quick_exit()In addition, there are a variety of ill-behaved and implementation-dependent ways of making a program crash (e.g., dividing a double by zero).If a program is terminated using the standard-library function exit(), the destructors for constructed static objects are called (§15.4.1, §16.2.12).
However, if the program is terminated usingthe standard-library function abort(), they are not. Note that this implies that exit() does not terminate a program immediately. Calling exit() in a destructor may cause an infinite recursion. Thetype of exit() is:void exit(int);444Source Files and ProgramsChapter 15Like the return value of main() (§2.2.1), exit()’s argument is returned to ‘‘the system’’ as the value ofthe program. Zero indicates successful completion.Calling exit() means that the local variables of the calling function and its callers will not havetheir destructors invoked.
Throwing an exception and catching it ensures that local objects areproperly destroyed (§13.5.1). Also, a call of exit() terminates the program without giving the callerof the function that called exit() a chance to deal with the problem. It is therefore often best to leavea context by throwing an exception and letting a handler decide what to do next. For example,main() may catch every exception (§13.5.2.2).The C (and C++) standard-library function atexit() offers the possibility to have code executed atprogram termination.
For example:void my_cleanup();void somewhere(){if (atexit(&my_cleanup)==0) {// my_cleanup will be called at normal termination}else {// oops: too many atexit functions}}This strongly resembles the automatic invocation of destructors for global variables at program termination (§15.4.1, §16.2.12). An argument to atexit() cannot take arguments or return a result, andthere is an implementation-defined limit to the number of atexit functions. A nonzero valuereturned by atexit() indicates that the limit is reached. These limitations make atexit() less usefulthan it appears at first glance.
Basically, atexit() is a C workaround for the lack of destructors.The destructor of a constructed statically allocated object (§6.4.2) created before a call ofatexit(f) will be invoked after f is invoked. The destructor of such an object created after a call ofatexit(f) will be invoked before f is invoked.The quick_exit() function is like exit() except that it does not invoke any destructors.
You registerfunctions to be invoked by quick_exit() using at_quick_exit().The exit(), abort(), quick_exit(), atexit(), and at_quick_exit() functions are declared in <cstdlib>.15.5 Advice[1][2][3][4][5][6][7]Use header files to represent interfaces and to emphasize logical structure; §15.1, §15.3.2.#include a header in the source file that implements its functions; §15.3.1.Don’t define global entities with the same name and similar-but-different meanings in different translation units; §15.2.Avoid non-inline function definitions in headers; §15.2.2.Use #include only at global scope and in namespaces; §15.2.2.#include only complete declarations; §15.2.2.Use include guards; §15.3.3.Section 15.5[8][9][10][11][12]Advice445#include C headers in namespaces to avoid global names; §14.4.9, §15.2.4.Make headers self-contained; §15.2.3.Distinguish between users’ interfaces and implementers’ interfaces; §15.3.2.Distinguish between average users’ interfaces and expert users’ interfaces; §15.3.2.Avoid nonlocal objects that require run-time initialization in code intended for use as part ofnon-C++ programs; §15.4.1.This page intentionally left blankPart IIIAbstraction MechanismsThis part describes C++’s facilities for defining and using new types.
Techniques commonly called object-oriented programming and generic programming are presented.Chapters1617181920212223242526272829ClassesConstruction, Cleanup, Copy, and MoveOperator OverloadingSpecial OperatorsDerived ClassesClass HierarchiesRun-Time Type InformationTemplatesGeneric ProgrammingSpecializationInstantiationTemplates and HierarchiesMetaprogrammingA Matrix Design448Abstraction MechanismsPart III‘‘... there is nothing more difficult to carry out, nor more doubtful of success, nor moredangerous to handle, than to initiate a new order of things.
For the reformer makesenemies of all those who profit by the old order, and only lukewarm defenders in allthose who would profit by the new order...’’— Niccolò Machiavelli (‘‘The Prince’’ §vi)16ClassesThose types are not “abstract”;they are as real as int and float.– Doug McIlroy••••IntroductionClass BasicsMember Functions; Default Copying; Access Control; class and struct; Constructors; explicitConstructors; In-Class Initializers; In-Class Function Definitions; Mutability; Self-Reference; Member Access; static Members; Member TypesConcrete ClassesMember Functions; Helper Functions; Overloaded Operators; The Significance of ConcreteClassesAdvice16.1 IntroductionC++ classes are a tool for creating new types that can be used as conveniently as the built-in types.In addition, derived classes (§3.2.4, Chapter 20) and templates (§3.4, Chapter 23) allow the programmer to express (hierachical and parametric) relationships among classes and to take advantageof such relationships.A type is a concrete representation of a concept (an idea, a notion, etc.).
For example, the C++built-in type float with its operations +, −, ∗, etc., provides a concrete approximation of the mathematical concept of a real number. A class is a user-defined type. We design a new type to providea definition of a concept that has no direct counterpart among the built-in types.
For example, wemight provide a type Trunk_line in a program dealing with telephony, a type Explosion for a videogame, or a type list<Paragraph> for a text-processing program. A program that provides types thatclosely match the concepts of the application tends to be easier to understand, easier to reasonabout, and easier to modify than a program that does not. A well-chosen set of user-defined types450ClassesChapter 16also makes a program more concise.