Chapter 29. C++’s Dustier Corners

There be of them that have left a name behind them.

Ecclesiasticus XLIV, 1

This chapter describes the few remaining features of C++ that are not described in any of the previous chapters. It is titled "C++’s Dustier Corners" because these statements are hardly ever used in real programming.

do/while

The do/while statement has the following syntax:

do { 
        statement; 
                       statement; 
} while    (expression); 

The program loops, tests the expression, and stops if the expression is false (0).

Tip

This construct always executes at least once.

do/while is not frequently used in C++ because most programmers prefer to use a while/break combination.

goto

All the sample programs in this book were coded without using a single goto. In actual practice I find I use a goto statement about once every other year. For those rare times that a goto is necessary, its syntax is:

goto label;

where label is a statement label. Statement labels follow the same naming convention as variable names. Labeling a statement is done as follows:

               label: statement;

For example:

    for (x = 0; x < X_LIMIT; ++x) { 
        for (y = 0; y < Y_LIMIT; ++y) { 
            assert((x >= 0) && (x < X_LIMIT));
            assert((y >= 0) && (y < Y_LIMIT));
            if (data[x][y] == 0) 
                goto found; 
        } 
    } 
    std::cout << "Not found
"; 
    exit(8); 

found: 
    std::cout << "Found at (" << x << ',' << y << ")
";

One of the things you don’t want to do is to use a goto statement to skip over initialization code. For example:

{
    goto skip_start;

    {
        int first = 1;

skip_start:
       printf("First is %d
", first);
    }
}

This confuses the compiler and should be avoided.

Question 29-1: Why does Example 29-1 not print an error message when an incorrect command is entered? Hint: There is a reason I put this in the goto section.

Example 29-1. def/def.cpp
#include <iostream>
#include <cstdlib>              

int main(  )
{
    char  line[10];

    while (true) {
        std::cout << "Enter add(a), delete(d), quit(q): ";
        std::cin.getline(line, sizeof(line));

        switch (line[0]) {
        case 'a':
            std::cout << "Add
";
            break;
        case 'd':
            std::cout << "Delete
";
            break;
        case 'q':
            std::cout << "Quit
";
            exit(0);
        default:
            std::cout << "Error:Bad command " << line[0] << '
';
            break;
        }
    }
    return (0);
}

The ?: Construct

The ? and : operators work much the same as if/then/else. Unlike if / then/else, the ?: operators can be used inside of an expression. The general form of ?: is:

(expression) ? expr1 : expr2;

For example, the following assigns to amount_owed the value of the balance or zero, depending on the amount of the balance:

amount_owed = (balance < 0) ? 0 : balance;

The following macro returns the minimum of its two arguments:

#define min(x, y) ((x) < (y) ? (x) : (y))

Tip

The C++ library has a perfectly good min function so you don’t have to define it yourself. Use the template for safety and efficiency.

The Comma Operator

The comma operator (,) can be used to group statements. For example:

if (total < 0) { 
    std::cout << "You owe nothing
"; 
    total = 0; 
}

can be written as:

if (total < 0)  
    std::cout << "You owe nothing
", total = 0;

In most cases, { } should be used instead of a comma. About the only place the comma operator is useful is in a for statement. The following for loop increments two counters, two and three, by 2 and 3:

for (two = 0, three = 0; 
     two < 10; 
     two += 2, three += 3) 
         std::cout << two << ' ' << three << '
';

Overloading the ( ) Operator

The ( ) operator can be overloaded for a class to give the class a “default” function. For example:

class example {
    public:
        int operator (  ) (int i) {
            return (i * 2);
        }
};
// ....
     example example_var;

     j = example_var(3);    // j is assigned the value 6 (3 * 2)

Overloading the ( ) operator is rarely done. Normal member functions can easily be used for the same purpose but have the advantage of providing the user with a function name.

Pointers to Members

The operator ::* is used to point to a member of a class. For example, in the following code we declare data_ptr as a “pointer to an integer in sample”:

class sample {
    public:
        int i;    // A couple of member variables
        int j;
};

int sample::* data_ptr;

Now data_ptr can point to either the i or the j member of sample. (After all, they are the only integer members of sample.)

Let’s set data_ptr so it points to the member i:

    data_ptr = &sample::i;

An ordinary pointer identifies a single item. A member pointer identifies a member but does not identify an individual variable. All we’ve done is set data_ptr to a member of sample. data_ptr does not point to a particular integer.

To use data_ptr you need to tell it which object you want:

sample a_sample; // A typical sample
sample b_sample;

std::cout << a_sample.*data_ptr << '
';
std::cout << b_sample.*data_ptr << '
';

The line:

std::cout << a_sample.*data_ptr << '
';

tells C++ that we want to print an element of the variable a_sample. The variable data_ptr points to an integer member of sample. (The members i and j are our only two integer members.)

There is a shorthand notation for use with class pointers as well:

sample *sample_ptr = &sample1;

std::cout << sample_ptr->*data_ptr << '
';

The syntax for pointers to members is a little convoluted and not terribly useful. I’ve only seen it used once by an extremely clever programmer. (The first maintenance programmer who got the code immediately ripped it out anyway.)

The asm Statement

Sometimes there are things that you just can’t do in C++. In those rare cases, the asm statement comes to the rescue. It lets you specify assembly code directly. The general form of this statement is:

asm("assembly statement")

Needless to say, this statement is highly nonportable.

The mutable Qualifier

Normally you can’t modify the members of a constant object. If a member is declared mutable, it can be modified. For example:

class sample {
    public:
       int i1;
       mutable float f1;
// ...
};

const sample a_sample;

a_sample.i1 = 1;    // Illegal, attempt to modify a constant
a_sample.f1 = 1.0;  // Legal. f1 is mutable.

Run Time Type Identification

The typeid function returns information about the type of an expression. The result is of type std::type_info. This class contains the member function name( ) and returns the name of the type.

For example:

    type_info info = typeid(1.0 + 2);

    std::cout << "Result is of type " << info.name(  ) << endl;

Trigraphs

C++ has a number of trigraphs designed for people whose keyboard is missing some of the special characters found on most computer keyboards. For example, ??= can be used instead of #.

Answers to Chapter Questions

Answer 29-1: The compiler didn’t see our default line because we misspelled “default” as “defualt.” This was not flagged as an error because “defualt” is a valid goto label. That’s why when we compile the program we get the following warning:

def.c(26): warning: defualt unused in function main

This means we defined a label for a goto, but never used it.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.139.8