EXPLORATION 4

image

Strings

In earlier Explorations, you used quoted character strings as part of each output operation. In this Exploration, you will begin to learn how to make your output a little fancier by doing more with strings. Start by reading Listing 4-1.

Listing 4-1.  Different Styles of String Output

#include <iostream>
 
int main()
{
   std::cout << "Shape Sides " << "----- ----- ";
   std::cout << "Square " << 4 << ' ' <<
                "Circle ? ";
}

Predict the output from the program in Listing 4-1. You may already know what means. If so, this prediction is easy to make. If you don’t know, take a guess.

_____________________________________________________________

Now check your answer. Were you correct? So what does mean?

_____________________________________________________________

Inside a string, the backslash () is a special, even magical, character. It changes the meaning of the character that follows it. You have already seen how starts a new line. Now you know that is a horizontal tab: that is, it aligns the subsequent output at a tab position. In a typical console, tab stops are set every eight character positions.

How should you print a double-quote character in a string?

_____________________________________________________________

Write a program to test your hypothesis then run the program. Were you correct?

_____________________________________________________________

Compare your program with Listing 4-2.

Listing 4-2.  Printing a Double-Quote Character

#include <iostream>
 
int main()
{
   std::cout << "" ";
}

In this case, the backslash turns a special character into a normal character. C++ recognizes a few other backslash character sequences, but these three are the most commonly used. (You’ll learn a few more when you read about characters in Exploration 17.)

Now modify Listing 4-1 to add Triangle to the list of shapes.

What does the output look like? A tab character does not automatically align a column but merely positions the output at the next tab position. To align columns, you have to take control of the output. One easy way to do this is to use multiple tab characters, as shown in Listing 4-3.

Listing 4-3.  Adding a Triangle and Keeping the Columns Aligned

 1 #include <iostream>
 2
 3 int main()
 4 {
 5    std::cout << "Shape Sides " <<
 6                 "----- ----- ";
 7    std::cout << "Square " << 4 << ' ' <<
 8                 "Circle ? "
 9                 "Triangle " << 3 << ' ';
10 }

I played a trick on you in Listing 4-3. Look closely at the end of line 8 and the start of line 9. Notice that the program lacks an output operator (<<) that ordinarily separates all output items. Any time you have two (or more) adjacent character strings, the compiler automatically combines them into a single string. This trick applies only to strings, not to characters. Thus, you can write lines 8 and 9 in many different ways, all meaning exactly the same thing.

std::cout << "
Circle		?
" "Triangle	" << 3 << '
';
std::cout << " Circle ? Triangle " << 3 << ' ';
std::cout << " " "Circle" " ? " "Triangle" " " << 3 << ' ';

Choose the style you like best and stick with it. I like to make a clear break after each newline, so the human who reads my programs can clearly distinguish where each line ends and a new line begins.

You may be asking yourself why I bothered to print the numbers separately, instead of printing one big string. That’s a good question. In a real program, printing a single string would be best, but in this book, I want to keep reminding you about the various ways you can write an output statement. Imagine, for example, what you would do if you didn’t know beforehand the name of a shape and its number of sides. Perhaps that information is stored in variables, as shown in Listing 4-4.

Listing 4-4.  Printing Information That Is Stored in Variables

 1 #include <iostream>
 2 #include <string>
 3
 4 int main()
 5 {
 6    std::string shape{"Triangle"};
 7    int sides{3};
 8
 9    std::cout << "Shape Sides " <<
10                 "----- ----- ";
11    std::cout << "Square " << 4 << ' ' <<
12                 "Circle ? ";
13    std::cout << shape << ' ' << sides << ' ';
14 }

The type of a string is std::string. You must have #include <string> near the top of your program to inform the compiler that you are using the std::string type. Line 6 shows how to give an initial value to a string variable. Sometimes, you want the variable to start out empty. How do you think you would define an empty string variable?

_____________________________________________________________

_____________________________________________________________

Write a program to test your hypothesis.

If you have trouble verifying that the string is truly empty, try printing the string between two other, nonempty strings. Listing 4-5 shows an example.

Listing 4-5.  Defining and Printing an Empty String

1 #include <iostream>
2 #include <string>
3
4 int main()
5 {
6    std::string empty;
7    std::cout << "|" << empty << "| ";
8 }

Compare your program with Listing 4-5. Which do you prefer? ________________

Why?

_____________________________________________________________

_____________________________________________________________

Line 6 does not provide an initial value for the variable, empty. You learned in Exploration 3 that omitting the initial value leaves the variable uninitialized, which would be an error, because no other value is assigned to empty. You are not allowed to print or otherwise access the value of an uninitialized variable. But std::string is different. The lack of an initializer in this case is the same as empty braces; namely, the variable is initialized to an empty string.

When you define a string variable with no initial value, C++ guarantees that the string is initially empty. Modify Listing 4-4 so the shape and sides variables are uninitialized. Predict the output of the program.

_____________________________________________________________

_____________________________________________________________

What happened? Explain.

_____________________________________________________________

_____________________________________________________________

_____________________________________________________________

Your program should look like Listing 4-6.

Listing 4-6.  Demonstrating Uninitialized Variables

 1 #include <iostream>
 2 #include <string>
 3
 4 int main()
 5 {
 6    std::string shape;
 7    int sides;
 8
 9    std::cout << "Shape Sides " <<
10                 "----- ----- ";
11    std::cout << "Square " << 4 << ' ' <<
12                 "Circle ? ";
13    std::cout << shape << ' ' << sides << ' ';
14 }

When I run Listing 4-6, I get different answers, depending on which compilers and platforms I use. Most compilers issue warnings, but will still compile the program, so you can run it. One of the answers I get is the following:

Shape             Sides
-----          -----
Square         4
Circle         ?
        4226851

With another compiler on another platform, the final number is 0. Yet another compiler’s program prints -858993460 as the final number. Some systems might even crash instead of printing the value of shape or sides.

Isn’t that curious? If you do not supply an initial value for a variable of type std::string, C++ makes sure the variable starts out with an initial value—namely, an empty string. On the other hand, if the variable has type int, you cannot tell what the initial value will actually be, and in fact, you cannot even tell whether the program will run. This is known as undefined behavior. The standard permits the C++ compiler and runtime environment to do anything, absolutely anything, when confronted with certain erroneous situations, such as accessing an uninitialized variable.

A design goal of C++ is that the compiler and library should not do any extra work if they can avoid it. Only the programmer knows what value makes sense as a variable’s initial value, so assigning that initial value must be the programmer’s responsibility. After all, when you are putting the finishing touches on your weather simulator (the one that will finally explain why it always rains when I go to the beach), you don’t want the inner loop burdened by even one wasteful instruction. The flip side of that performance guarantee is an added burden on the programmer to avoid situations that give rise to undefined behavior. Some languages help the programmer avoid problem situations, but that help invariably comes with a performance cost.

So what’s the deal with std::string? The short answer is that complicated types, such as strings, are different from the simple, built-in types. For types such as std::string, it is actually simpler for the C++ library to provide a well-defined initial value. Most of the interesting types in the standard library behave the same way.

If you have trouble remembering when it is safe to define a variable without an initial value, play it safe and use empty braces:

std::string empty{};
 
int zero{};

I recommend initializing every variable, even if you know the program will overwrite it soon, such as the input loops we used earlier. Omitting initialization in the name of “performance” rarely improves performance and always impairs readability. The next Exploration demonstrates the importance of initializing every variable.

OLD-FASHIONED INITIALIZATION

The brace style of initializing all variables was introduced in C++ 11, so code that predates C++ 11 (or new code that was written by programmers who learned C++ prior to C++ 11 and still haven’t come to grips with the new style of initialization) uses different ways to initialize variables.

A common way to initialize integers, for example, uses an equal sign. It looks like an assignment statement, but it isn’t. It defines and initializes a variable.

int x = 42;

You can also use parentheses:

int x(42);

The same is true for many standard-library types:

std::string str1 = "sample";
std::string str2("sample");

Other types require parentheses and don’t work with an equal sign. Other types used curly braces before C++ 11. Equal signs, parentheses, and curly braces all had different rules, and it was hard for beginners to understand the subtle difference between the equal sign and parentheses for initialization.

So the standardization committee made an effort to define a single, uniform initialization style in C++ 11, which is what I presented in this Exploration.

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

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