How to Write elegant code (2) -- # define? Const? Or enum?

Source: Internet
Author: User

// ================================================ ========================================
// Title:
// How to Write elegant code (2) -- # define? Const? Or enum?
// Author:
// Norains
// Date:
/// Tuesday 21-July-2009
// Environment:
// Wince5.0 + vs2005
// ================================================ ========================================

# Define, const, and Enum: What are the associations between the three? One is macro definition, the other is static modifier, and the last is Enumeration type. Is it a bit like a wheat peel? If we narrow down the scope and let all three be limited to "fixed values", then the relationship will be on paper-at least, something in common.

Before explaining what a "fixed value" is, let's first understand what an "odd number" is ". Too many principles have been warned, and less "odd" is used, because this will cause the code to be unable to be maintained. It sounds as mysterious as a fortune teller, but its semantics is so simple. The following two code segments are just an odd number:
Code snippet 1: <br/> switch (mode) <br/>{< br/> case 1: <br/> // to do someting. <br/> break; <br/> case 2: <br/> // to do someting. <br/> break; <br/> case 3: <br/> // to do someting. <br/> break; <br/>}</P> <p> code Segment 2: <br/> switch (Mode) <br/>{< br/> case sleep: <br/> // to do someting. <br/> break; <br/> case power_off: <br/> // to do someting. <br/> break; <br/> case power_on: <br/> // to do someting. <br/> break; <br/>}

 

Obviously, code segment 2 is much more readable than code segment 1. In these two instances, "1", "2", and "3" are called odd numbers, while "Sleep", "power_off", and "power_on" are fixed values. There are three methods for defining fixed values in C ++: # define, const, and enum.

Scott Meyers, author of effect c ++, once suggested that const should be used to replace # define with Const. This sentence is not without reason. On the other hand, # define and const can be used in many places.

For example:

Const DWORD default_volume = 0 xFFFF; <br/> // # define default_volume 0 xFFFF </P> <p>... </P> <p> m_dwvolume = default_volume;

 

Whether you use const or # define to define default_volume, there is no essential change in the statement m_dwvolume = default_volume. So, does it mean that whether to use # define or const depends entirely on the mood at the time? Naturally, the answer is no. Otherwise, this article will become a lyrical prose.

# Define has a fatal defect and is not limited by scope. Any code after # define can directly use the value defined by # define.

We often write such a function to obtain the DWORD Value of a device. However, this function does not return the bool type to indicate success or failure, but uses another method: When the read is successful, the returned value is a specific device-related value; when the read fails, the return value is the default value. It sounds a bit strange, and I doubt under what circumstances this design will be used. However, the topic of this article does not discuss what the function can do or where it should appear, we only need to know that there is such a function.

Let's assume that the function prototype is as follows:

DWORD getdevdw (handle hdev, DWORD dwerror );

 

The call is also simple:

DWORD dwval = getdevdw (hdev, error_value );

 

In this example, if the value of dwval is equal to error_value, it means that getdevdw fails to be called. If it is not equal to error_value, the call is successful.

Now we have two functions to obtain information about the two devices. In the following example, we use # define to define the fixed value:

Void getdev1info () <br/>{< br/> .... </P> <p> # define error_value 0 <br/> getdevdw (null, error_value); </P> <p>... <br/>}</P> <p> void getdev2info () <br/>{< br/> .... </P> <p> # define error_value 2 <br/> getdevdw (null, error_value); </P> <p>... <br/>}

 

It seems like everything is good, isn't it? Unfortunately, there will be a warning in the Compilation: 'error _ value': Macro redefinition.

The root cause of the problem is that the # define value has no scope concept. Worse, the error_value used in the getdev2info function is not the expected 2, but the 0 defined in getdev1info. Oh, my God, there's nothing worse than that.

To completely solve this warning, we can do some additional work in the getdev2info function:

Void getdev2info () <br/>{< br/> .... </P> <p> # ifdef error_value <br/> # UNDEF error_value <br/> # endif </P> <p> # define error_value 2 <br/> getdevdw (null, error_value); </P> <p>... <br/>}

 

The problem is solved, and the warning is gone, but the code is ugly.

Another way is to change the fixed value name:

Void getdev1info () <br/>{< br/> .... </P> <p> # define dev1_error_value 0 <br/> getdevdw (null, dev1_error_value); </P> <p>... <br/>}</P> <p> void getdev2info () <br/>{< br/> .... </P> <p> # define dev2_error_value 2 <br/> getdevdw (null, dev2_error_value); </P> <p>... <br/>}

 

Similarly, the problem is solved, the warning is gone, and the code is not ugly. The only problem left over is that if there are many functions like this, we need to try our best to choose a unique name for each fixed error value. Well, this is not a good task for our lazy people. In this case, why not use const?

Void getdev1info () <br/>{< br/>... </P> <p> const DWORD error_value = 0; <br/> getdevdw (null, error_value); </P> <p> .... <br/>}</P> <p> void getdev2info () <br/>{< br/>... </P> <p> const DWORD error_value = 2; <br/> getdevdw (null, error_value); </P> <p>... <br/>}

 

That's all. Because const DWORD declares a local variable and is limited by the scope, we can use the same fixed value name in getdev1info and getdev2info.

This example may not be enough to persuade you to replace # define with const, so you should reverse this idea in the following example-maybe you have already met this example.

We have two classes to control the accent and power amplifier of a car. Both classes need to define max_volume in the header file for the user to call, but unfortunately, the stress and the max_volume value of the power amplifier are different.

If # define is used, we may write this in the header file:

/// // <Br/>/ /bass. h <br/> # define max_volume 15

/// // <Br/>/ /amplifier. h <br/> # define max_volume 30

 

When the two header files are not used at the same time, everything goes smoothly, right?

But if I need to control two volumes at the same time, we need to include these two files at the same time. Such calls should be familiar to everyone:

# Include "Bass. H" <br/> # include "amplifier. H"

 

The problem is obvious: serious warnings or compilation failure.

To solve this problem, we can only ask for const. However, if the statement is as follows:

/// // <Br/>/ /bass. h <br/> const DWORD max_volume = 15;

/// // <Br/>/ /amplifier. h <br/> const DWORD max_volume = 30;

 

The problem still remains the same as # define, without any fundamental changes. At this time, we can only ask for namespace.

/// // <Br/>/ /bass. h <br/> namespace bass <br/>{< br/> const DWORD max_volume = 15; <br/> };

/// // <Br/>/ /amplifier. h <br/> namespace amplifier <br/>{< br/> const DWORD max_volume = 30; <br/>}

 

If the namespace is not omitted using, we can try the following code:

DWORD dwbass = Bass: max_volume; <br/> DWORD dwamplifier = amplifier: max_volume;

 

In this example, the namespace serves as a marker to indicate the category of the current max_volume, which is an unexpected result.

If I see this, someone may ask, can I use the namespace + # define method? Unfortunately, the answer is no. As mentioned above, # define is not limited by the scope, so a simple namespace cannot intercept # define this beast.

So far, we can conclude that we should replace # define with const without Conditional compilation and fixed values.

Based on this principle, we will leave # define aside from the following discussions and use only Const.

Let's look back at the original example and encapsulate it as a function:

Bool switchmode (DWORD mode) <br/>{< br/>... </P> <p> switch (mode) <br/> {<br/> case sleep: <br/> // to do someting. <br/> break; <br/> case power_off: <br/> // to do someting. <br/> break; <br/> case power_on: <br/> // to do someting. <br/> break; <br/>}</P> <p>... <br/>}< br/>

 

The following fixed values are defined elsewhere in the Code:

Const DWORD sleep = 0x00; <br/> const DWORD power_off = 0x02; <br/> const DWORD power_on = 0x03;

 

When calling:

Switchmode (sleep); </P> <p>... </P> <p> switchmode (power_off); </P> <p>...

 

Very good, very beautiful, isn't it?

However, this cannot ensure that the user does not call the code in this way:

Switchmode (0x100 );

 

0x100 is not the expected value. The switchmode function does not process the value, but it complies with the compiler specification, it will enable this code to pass compilation without warning and without any errors.

Some people may say, who would be so stupid? Instead, assign a value using 0x100? This is true. The probability of directly using 0 x is indeed too small.

However, we cannot deny that there is a possibility that another function has a fixed value defined as follows:

Const DWORD file_mode = 0x100;

 

However, when we were dizzy, we might be drunk and misuse this parameter:

Switchmode (file_mode );

 

For the compiler, both 0x100 and file_mode do not make much sense, so this pathological code can easily be detected by the compiler. For people, because a fixed value has been used, and subconsciously think that this parameter is correct. Both the compiler and us are properly fooled.

So, is there a way for the compiler to prompt users with warnings or errors if this value is not what we want during compilation?

Everything is possible! However, at this time, we cannot use const, but must replace enum.

First, use Enum to define a fixed value:

Enum mode <br/>{< br/> sleep, <br/> power_off, <br/> power_on, <br/> };

 

The function declaration is changed as follows:

Bool switchmode (mode Mode)

 

The call is also the same as before:

Switchmode (sleep); </P> <p>... </P> <p> switchmode (power_off); </P> <p>...

 

The only difference is that if you call:

Switchmode (0x100); // compilation fails at this time <br/> switchmode (file_mode); // compilation fails at this time

 

Then the compiler will not hesitate to complain: cannot convert parameter 1 from 'int' to 'Mode '.

Well, the compiler is our first firewall to exclude irrelevant values that we don't need. Isn't it beautiful?

Of course, it is not impossible to force the compiler to pass a strange value:

Switchmode (static_cast <mode> (0x100 ));

 

Although 0x100 is not in the mode range, it still passes the compiler detection. There is no way to do this. However, how many situations will this extreme genius approach come into being?


Finally, we will summarize the following:

1. Just declare a single fixed value and use const as much as possible.

2. If a set of fixed values are associated with each other, Enum is used.

3. Conditional compilation is not involved, but # define is not used if fixed values are defined.

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.