Valid tive C ++, 3rd edition, item 2: replace # defines with consts, enums, and inlines

Source: Internet
Author: User
Tags constant definition

Item 2: replaced by consts, enums, and inlines # defines

By Scott Meyers

Translator: fatalerror99 (itepub's nirvana)

Release: http://blog.csdn.net/fatalerror99/

It may be better to change the name of this item to "replace Preprocessor with compiler (compiler)", because # define is not considered part of the language. This is one of many of its problems. When you do this as follows:

# Define aspect_ratio 1.653

Compiler (compiler) may not have seen this symbol name aspect_ratio at all. Before Compiler (compiler) obtains the source code, this name has been removed by Preprocessor. As a result, the name aspect_ratio may not be added to the symbol table (symbol table ). If a constant (constant) error is found during compilation, you may be confused because the error message may replace aspect_ratio with 1.653. If aspect_ratio is not defined in the header file, you may have no clue about the source of 1.653, And you will waste time tracking it. The same problem occurs in the symbolic debugger (symbol debugger), because the name may not be added to the symbol table ).

The solution is to replace macro (macro) with constant (constant ):

Const double aspectratio = 1.653; // uppercase names are usually
// Macros, hence the name change

As a language constant (a constant at the language level), aspectratio is explicitly identified by compilers (compiler) and indeed added to the symbol table (symbol table ). In addition, for floating point constant (floating point constant) (for example, in this example), using constant (constant) produces less code than using # define. This is because Preprocessor blindly replaces macro name (macro name) aspect_ratio with 1.653, resulting in multiple 1.653 copies in your object code (target code, if you use constant (constant) aspectratio, no more than one copy will be generated.

When using constant (constant) instead of # defines, two special cases are worth mentioning. The first is the definition of constant pointers (constant pointer. Because constant definitions (constant definition) is usually put in header files (so that they can be included in multiple source files (source files), except for pointer (pointer) the target is a constant,Pointer(Pointer) itself is declared as const is more important. For example, when defining a constant char *-Based String (char *-Based String constant) in the header file, you must write the const twice:

ConstChar *ConstAuthorname = "Scott Meyers ";

For a complete discussion of the meaning and use of const (especially when combined with pointers (pointer), see item 3. However, it is mentioned that string objects (object) is generally more desirable than its char *-based (char * based) ancestor. Therefore, A better definition of authorname is as follows:

Const STD: StringAuthorname ("Scott Meyers ");

The second special case involves class-specific constants (Class (internal dedicated) constants ). To limit the scope of a constant (constant) to a class, you must use it as a member (member) of a class ), to ensure that it only has one constant (constant) Copy at most, you must declare it asStaticMember (static member ).

Class gameplayer {

PRIVATE:

Static const int numturns = 5;// Constant Declaration

Int scores [numturns]; // use of constant
...
};

You can see only numturnsDeclaration(Declaration), rather than definition (Definition ). Generally, C ++ requires you to provide a definition (Definition) for everything you use, but a static (static) integral type (integer family) (for example: integers (integer), chars, bools) Class-specific constants (class constants) is an exception. As long as you do not get their address, you can declare and use it without providing its definition ). If you want to obtain the address (address) of a class constant (class constant), or the compiler (compiler) you are using, you have not obtained the address (address) if the definition is not correctly required, you can provide an independent definition as follows ):

Const int gameplayer: numturns; // definition of numturns; see
// Below for why no value is given

You should put it in an implementation file instead of a header file. Because the initial value (initial value) of Class constants (class constants) is provided at the time of declaration (for example, numturns is initialized to 5 at the time of definition ), therefore, no initial value (Initial Value) is allowed in the definition ).

Note: By the way, there is no way to use # define to create a class-specific constant (class constant), because # defines does not consider scope (scope of action ). Once a macro (macro) is defined, it will affect your code in a wide range (unless it exists somewhere later # undefed ). This means that # defines cannot be used not only for class-specific constants (class constants), but also does not provide any form of encapsulation (encapsulation), that is, there is no "private" (private) # define. Of course, const data members (const data member) can be encapsulated, as is numturns.

The older compilers (compiler) may not accept the above syntax because it is used to deem it illegal to obtain the initial value (Initial Value) When declaring a static class member (static class member. Moreover, in-class initialization (class initialization) is only allowed for integral types (integer family) and constants (constants. If the preceding syntax cannot be used, you can place the initial value (Initial Value) in the definition:

Class costestimate {

PRIVATE:

Static const double fudgefactor;// Declaration of static class
... // Constant; goes in header file
};

Const double// Definition of static class

Costestimate: fudgefactor = 1.35;// Constant; goes in impl. File

This is all you have to do. The only exception is when the value of a class constant (the value of a class constant) is required during the compilation of the class. For example, if you declare array (array) gameplayer before :: when scores (Compilers (compiler) is used, the size (size) of Array (array) must be known during compilation )). If compilers (compiler) (incorrectly) disables this initial values (initial value) of static integral class constants (static integer family constants) in-class specification (specification), an acceptable alternative is affectionately (not scornful) nicknamed "The Enum hack ". This technology is supported by the fact that the value of an enumerated type (Enumeration type) can be used in a place where ints is needed. Therefore, gameplayer can be defined as follows:

Class gameplayer {

PRIVATE:

Enum {numturns = 5 };// "The Enum hack"-makes
// Numturns a symbolic name for 5

Int scores [numturns]; // fine
...
};

The enum hack has several reasons to be known. First, the enum hack behavior is more like a # define than a const in several aspects, and sometimes this is what you need. For example, a const address can be obtained legally, but an Enum address cannot be obtained legally ), this is just as legal as obtaining a # Define address (address ). If you don't want people to get the pointer (pointer) or reference (reference) of your integral constants (integer family constant), Enum (enumeration) is a good way to force this constraint. (For more ways to enforce design constraints by coding, see item 18 .) Similarly, the used compilers (compiler) will not be const objects (const object) of the integral types (integer family type) allocate extra memory (unless you have created a pointer or reference to this object), and even compilers (compiler) will be happy, you will never be happy with such objects (object) allocate extra memory. Such as # defines and enums (enumeration) will never cause this unnecessary memory allocation (unnecessary memory allocation ).

The second reason you need to know the enum hack is purely pragmatic. A lot of code is using it, so you need to know it when you see it. In fact, the enum hack is a basic technique of template metaprogramming (template metaprogramming) (see item 48 ).

Back to Preprocessor, # Another common (bad) usage of the define command is to implement macros (macro) that looks like a function, but does not cause the overhead of a function call ). The following is a macro (macro) that calls function F with a large macro parameter ):

// Call f with the maximum of A and B

# Define call_with_max (a, B) f (a)> (B )? (A): (B ))

This macro (macro) has countless shortcomings, which can be a headache.

Whenever you write such a macro (macro), you must remember to enclose all arguments (parameters) in macro body (macro) with parentheses. Otherwise, when someone else calls your macro (macro) in the expression, you will be in trouble. However, even if you do, you will still see unexpected things happen:

Int A = 5, B = 0;

Call_with_max (++, B); // A is incremented twice

Call_with_max (++, B + 10); // A is incremented once

Here, the number of times a increments before calling F depends on what it compares!

Fortunately, you do not have to deal with such cloud-based things. You can use an inline function (Inline Function) template to obtain macro (macro) efficiency, and completely predictable behavior and type security of common functions (see item 30 ):

Template <typename T> // because we don't

Inline void callwithmax (const T & A, const T & B) // know what t is, we

{// Pass by reference--

F (A> B? A: B); // const-see item 20

}

This template generates a set of functions, each of which obtains two objects of the same type and calls f using a larger one. In this way, you do not need to add brackets to parameters in the function body, or worry about the number of extra parameter resolutions. Besides, because callwithmax is a real function, it follows the function's scope and access rules. For example, a private inline function (Inline Function) of a class can get a correct understanding, but macro (macro) cannot do this.

To obtain the availability of consts, enums, and inlines, You need to minimize the use of Preprocessor (especially # define), but it cannot be completely eliminated. # Include is still the basic element, while # ifdef/# ifndef continues to play an important role. It's not the time to completely retire Preprocessor, but you should give it a long and frequent holiday.

Things to remember

  • For simple constants (simple constants), replace # defines with const objects (const object) or enums (enumeration.
  • For function-like macros (macro similar to a function), replace # defines With inline functions (inline function.
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.