B. Stroustrup - The C++ Programming Language (794319), страница 53
Текст из файла (страница 53)
Ifyou do, you can declare the enumerations without defining them until later. For example:enum Traffic_light : char { tl_red, tl_yellow, tl_green };// underlying type is charenum Color_code : char;// declarationvoid foobar(Color_code∗ p); // use of declaration// ...enum Color_code : char { red, yellow, green, blue }; // definitionIf you don’t specify the underlying type, you can’t declare the enum without defining it, and itsunderlying type is determined by a relatively complicated algorithm: when all enumerators are nonkknegative, the range of the enumeration is [0:2 -1] where 2 is the smallest power of 2 for which allk kenumerators are within the range.
If there are negative enumerators, the range is [-2 :2 -1]. Thisdefines the smallest bit-field capable of holding the enumerator values using the conventional two’scomplement representation. For example:enum E1 { dark, light };// range 0:1enum E2 { a = 3, b = 9 };// range 0:15enum E3 { min = −10, max = 1000000 }; // range -1048576:1048575The rule for explicit conversion of an integer to a plain enum is the same as for the class enumexcept that when there is no explicit underlying type, the result of such a conversion is undefinedunless the value is within the range of the enumeration.
For example:enum Flag { x=1, y=2, z=4, e=8 };// range 0:15Flag f0 {};Flag f1 = 5;Flag f2 = Flag{5};Flag f2 = static_cast<Flag>(5);Flag f3 = static_cast<Flag>(z|e);Flag f4 = static_cast<Flag>(99);// f0 gets the default value 0// type error: 5 is not of type Flag// error : no explicit conversion from int to Flag// OK: 5 is within the range of Flag// OK: 12 is within the range of Flag// undefined: 99 is not within the range of FlagBecause there is an implicit conversion from a plain enum to its underlying type, we don’t need todefine | to make this example work: z and e are converted to int so that z|e can be evaluated.
Thesizeof an enumeration is the sizeof its underlying type. If the underlying type isn’t explicitly specified, it is some integral type that can hold its range and not larger than sizeof(int), unless an enumerator cannot be represented as an int or as an unsigned int. For example, sizeof(e1) could be 1 ormaybe 4 but not 8 on a machine where sizeof(int)==4.224Structures, Unions, and EnumerationsChapter 88.4.3 Unnamed enumsA plain enum can be unnamed.
For example:enum { arrow_up=1, arrow_down, arrow_sideways };We use that when all we need is a set of integer constants, rather than a type to use for variables.8.5 Advice[1][2][3][4][5][6][7]When compactness of data is important, lay out structure data members with larger membersbefore smaller ones; §8.2.1.Use bit-fields to represent hardware-imposed data layouts; §8.2.7.Don’t naively try to optimize memory consumption by packing several values into a singlebyte; §8.2.7.Use unions to save space (represent alternatives) and never for type conversion; §8.3.Use enumerations to represent sets of named constants; §8.4.Prefer class enums over ‘‘plain’’ enums to minimize surprises; §8.4.Define operations on enumerations for safe and simple use; §8.4.1.9StatementsA programmer is a machinefor turning caffeine into code.– A programmer••••••••IntroductionStatement SummaryDeclarations as StatementsSelection Statementsif Statements; switch Statements; Declarations in ConditionsIteration StatementsRange-for Statements; for Statements; while Statements; do Statements; Loop exitgoto StatementsComments and IndentationAdvice9.1 IntroductionC++ offers a conventional and flexible set of statements.
Basically all that is either interesting orcomplicated is found in expressions and declarations. Note that a declaration is a statement andthat an expression becomes a statement when you add a semicolon at its end.Unlike an expression, a statement does not have a value. Instead, statements are used to specifythe order of execution. For example:a = b+c;if (a==7)b = 9;// expression statement// if-statement// execute if and only if a==9Logically, a=b+c is executed before the if, as everyone would expect. A compiler may reorder codeto improve performance as long as the result is identical to that of the simple order of execution.226StatementsChapter 99.2 Statement SummaryHere is a summary of C++ statements:statement:declarationexpressionopt ;{ statement-listopt }try { statement-listopt } handler-listcase constant-expression :default : statementbreak ;continue ;returnstatementexpressionopt ;identifier ;identifier : statementgotoselection-statementiteration-statementselection-statement:if ( condition ) statementif ( condition ) statement else statementswitch ( condition ) statementiteration-statement:while ( condition ) statementdo statement while ( expression ) ;for ( for-init-statement conditionopt ; expressionopt ) statementfor ( for-init-declaration : expression ) statementstatement-list:statement statement-listoptcondition:expressiontype-specifier declarator = expressiontype-specifier declarator { expression }handler-list:handler handler-listopthandler:catch (exception-declaration ) { statement-listopt }A semicolon is by itself a statement, the empty statement.Section 9.2Statement Summary227A (possibly empty) sequence of statements within ‘‘curly braces’’ (i.e., { and }) is called a blockor a compound statement.
A name declared in a block goes out of scope at the end of its block(§6.3.4).A declaration is a statement and there is no assignment statement or procedure-call statement;assignments and function calls are expressions.A for-init-statement must be either a declaration or an expression-statement. Note that both endwith a semicolon.A for-init-declaration must be the declaration of a single uninitialized variable.The statements for handling exceptions, try-blocks, are described in §13.5.9.3 Declarations as StatementsA declaration is a statement. Unless a variable is declared static, its initializer is executed wheneverthe thread of control passes through the declaration (see also §6.4.2). The reason for allowing declarations wherever a statement can be used (and a few other places; §9.4.3, §9.5.2) is to enable theprogrammer to minimize the errors caused by uninitialized variables and to allow better locality incode.
There is rarely a reason to introduce a variable before there is a value for it to hold. Forexample:void f(vector<string>& v, int i, const char∗ p){if (p==nullptr) return;if (i<0 || v.size()<=i)error("bad index");string s = v[i];if (s == p) {// ...}// ...}The ability to place declarations after executable code is essential for many constants and for single-assignment styles of programming where a value of an object is not changed after initialization.For user-defined types, postponing the definition of a variable until a suitable initializer is availablecan also lead to better performance. For example:void use(){string s1;s1 = "The best is the enemy of the good.";// ...}This requests a default initialization (to the empty string) followed by an assignment.
This can beslower than a simple initialization to the desired value:string s2 {"Voltaire"};The most common reason to declare a variable without an initializer is that it requires a statement228StatementsChapter 9to give it its desired value.
Input variables are among the few reasonable examples of that:void input(){int buf[max];int count = 0;for (int i; cin>>i;) {if (i<0) error("unexpected negative value");if (count==max) error("buffer overflow");buf[count++] = i;}// ...}I assume that error() does not return; if it does, this code may cause a buffer overflow. Often,push_back() (§3.2.1.3, §13.6, §31.3.6) provides a better solution to such examples.9.4 Selection StatementsA value can be tested by either an if-statement or a switch-statement:condition ) statementcondition ) statement else statementswitch ( condition ) statementif (if (A condition is either an expression or a declaration (§9.4.3).9.4.1 if StatementsIn an if-statement, the first (or only) statement is executed if the condition is true and the secondstatement (if it is specified) is executed otherwise.
If a condition evaluates to something differentfrom a Boolean, it is – if possible – implicitly converted to a bool. This implies that any arithmeticor pointer expression can be used as a condition. For example, if x is an integer, thenif (x) // ...meansif (x != 0) // ...For a pointer p,if (p) // ...is a direct statement of the test ‘‘Doesand is equivalent toppoint to a valid object (assuming proper initialization)?’’if (p != nullptr) // ...Note that a ‘‘plain’’ enum can be implicitly converted to an integer and then to aenum class cannot (§8.4.1).
For example:bool,whereas anSection 9.4.1ifStatements229enum E1 { a, b };enum class E2 { a, b };void f(E1 x, E2 y){if (x)// ...if (y)// ...if (y==E2::a)// ...}// OK// error: no conversion to bool// OKThe logical operators&& ||!are most commonly used in conditions. The operators && and || will not evaluate their second argument unless doing so is necessary. For example,if (p && 1<p−>count) // ...This tests 1<p−>count only if p is not nullptr.For choosing between two alternatives each of which produces a value, a conditional expression(§11.1.3) is a more direct expression of intent than an if-statement.
For example:int max(int a, int b){return (a>b)?a:b;}// return the larger of a and bA name can only be used within the scope in which it is declared. In particular, it cannot be usedon another branch of an if-statement. For example:void f2(int i){if (i) {int x = i+2;++x;// ...}else {++x; // error: x is not in scope}++x;// error: x is not in scope}A branch of an if-statement cannot be just a declaration.