C language programming skills about the difference between const and hashdefine

  • 2020-04-01 21:33:22
  • OfStack

# define ASPECT_RATIO 1.653

The compiler will never see the symbolic name of ASPECT_RATIO because it is removed by the preprocessor before the source code enters the compiler, so ASPECT_RATIO is not added to the list of symbols. If the code involved in this constant is compiling an error, it can be confusing because the error message refers to 1.653, not ASPECT_RATIO. If ASPECT_RATIO isn't defined in your own header file, you'll wonder where 1.653 came from and even spend time tracking it down. This problem also occurs in the symbol debugger because, again, the symbol name you write does not appear in the symbol list.
The solution to this problem is simple: instead of preprocessing macros, define a constant: & PI;

Const double ASPECT_RATIO = 1.653;


It works. But there are two special cases to note.

First, defining pointer constants is a little bit different. Because constant definitions are typically placed in header files (which many source files contain), it is important that Pointers are often const, in addition to the type they refer to. For example, to define a char* based string constant in the header file, you write const twice:


Const char * const authorName = "Scott Meyers";

For the meaning and use of const, especially in relation to Pointers, see article 21.  

In addition, defining constants for a class is generally convenient, with only a few differences. To restrict a constant to a class, first make it a member of the class. To ensure that a constant has at most one copy, define it as a static member:            

The class GamePlayer {
Private:
Static const int NUM_TURNS = 5; / / constant eclaration 
Int scores [NUM_TURNS]; / / the use of constant
.
};


Also, as you can see, the above statement is a declaration of NUM_TURNS, not a definition, so you must also define the class's static members in the class's implementation code file:


Const int GamePlayer: : NUM_TURNS; / / mandatory definition;
// goes in class imp.file


You needn't worry too much about such trifles. If you forget the definition, the linker will remind you.


Older compilers do not accept this syntax because it considers it illegal for a static member of a class to define an initial value when declared. Also, only integer types (such as int, bool, char, etc.) are allowed to be initialized in the class and can only be constants.
In the case that the above syntax cannot be used, the initial value can be assigned at definition time:
Class EngineeringConstants {// this goes in the class
Private: / / the header file
The static const double FUDGE_FACTOR;
.
};
// this goes in the class implementation file
Const double EngineeringConstants: : FUDGE_FACTOR = 1.35;


Most of the time that's all you have to do. The only exception to this is when your class needs a constant for that class at compile time, such as the declaration of the GamePlayer::scores array above (the compiler must know the size of the array during compilation). So, to compensate for compilers that (incorrectly) prohibit integer class constant initialization within a class, a method called "borrowing enum" can be used. This technique makes good use of the principle of using enumerated types when an int is needed, so GamePlayer can also be defined like this:
The class GamePlayer {
Private:
Enum {NUM_TURNS = 5} // "the enum hack" -- makes
// NUM_TURNS a symbolic name& cake;
/ / for 5
Int scores [NUM_TURNS]; / / fine
};


Unless you are using an old compiler (that is, written before 1995), you do not need to borrow enum. Of course, it's worth knowing that there is, because code that dates back a long time is not common.


Back to the preprocessing. Another common use of the #define directive is to use it to implement macros that look like functions without causing function calls. A typical example is to calculate the maximum value of two objects:
# define Max (a, b) ((a) > (b)? (a) : (b))


The sentence has so many flaws that the mere thought of it is more painful than driving on the highway at rush hour.
Whenever you write a macro like this, you must remember to put parentheses around each argument when you write the macro body; Otherwise, using an expression when someone calls your macro can cause a lot of trouble. But even if you do, strange things like the following will happen:


Int a = 5, b = 0;
Max (+ + a, b); // a has increased twice
Max (a, b + + + 10); // a has only increased by 1 time


In this case, what happens inside Max depends on what value it compares!
Fortunately, you don't have to put up with such stupid statements anymore. You can use normal functions to achieve macro efficiency, coupled with predictable behavior and type safety, which are inline functions (see article 33) :
Inline int Max (int a, int b) {return a > B? A: b; }
This is different from the macro above, however, because this version of Max can only handle int types. But templates can easily solve this problem:
Template< The class T>
Inline const T& Max (const T& a, const T& b)
{return a > B? A: b; }


This template produces a whole set of functions, each of which compares two objects that can be converted to the same type and returns a reference to a larger (constant) object. Since you do not know the type of T, passing the reference on return is more efficient (see article 22).


By the way, before you start templating useful generic functions like Max, check the standard libraries (see article 49) to see if they already exist. Take Max above, for example, and you'll be pleasantly surprised to find that you can enjoy the cool: Max is part of the C++ standard library.
With const and inline, you need less preprocessing, but you can't do without it. #include is still a long way off, and #ifdef/#ifndef also plays an important role in controlling compilation. Preconditioning can't retire yet, but you must plan to give it frequent sabbatical breaks


Related articles: