We also use a standard C technique for reporting program status to the calling context by returning different values from main( ). It is portable to use the statement return 0; to indicate success, but there is no portable value to indicate failure. For this reason we use the macro declared for this very purpose in <cstdlib>: EXIT_FAILURE. As a matter of consistency, whenever we use EXIT_FAILURE we also use EXIT_SUCCESS, even though the latter is always defined as zero.
Summary
C++ string objects provide developers with a number of great advantages over their C counterparts. For the most part, the string class makes referring to strings through the use of character pointers unnecessary. This eliminates an entire class of software defects that arise from the use of uninitialized and incorrectly valued pointers. C++ strings dynamically and transparently grow their internal data storage space to accommodate increases in the size of the string data. This means that when the data in a string grows beyond the limits of the memory initially allocated to it, the string object will make the memory management calls that take space from and return space to the heap. Consistent allocation schemes prevent memory leaks and have the potential to be much more efficient than "roll your own" memory management.
The string class member functions provide a fairly comprehensive set of tools for creating, modifying, and searching in strings. String comparisons are always case sensitive, but you can work around this by copying string data to C-style null-terminated strings and using case-insensitive string comparison functions, temporarily converting the data held in string objects to a single case, or by creating a case-insensitive string class that overrides the character traits used to create the basic_string object.
Exercises
1. Write a program that reverses the order of the characters in a string.
18. A palindrome is a word or group of words that read the same forward and backward. For example "madam" or "wow." Write a program that takes a string argument from the command line and prints whether the string was a palindrome or not.
19. Make your program from exercise 2 return true even if symmetric letters differ in case. For example, "Civic" would still return true although the first letter is capitalized.
20. Make your program from exercise 3 report true even if the string contains punctuation and spaces. For example "Able was I, ere I saw Elba." would report true.
21. Using the following strings and only chars (no string literals or magic numbers):
string one("I walked down the canyon with the moving mountain bikers.");
string two("The bikers passed by me too close for comfort."); string three("I went hiking instead.")
produce the following sentence:
"I moved down the canyon with the mountain bikers. The mountain bikers passed by me too close for comfort. So I went hiking instead."
22. Write a program named replace that takes three command-line arguments representing an input text file, a string to replace (call it from), and a replacement string (call it to). The program should write a new file to standard output with all occurrences of from replaced by to.
23. Repeat the previous exercise but replace all instances of from regardless of case.
4: Iostreams
You can do much more with the general I/O problem than just take standard I/O and turn it into a class.
Wouldn’t it be nice if you could make all the usual "receptacles"—standard I/O, files, and even blocks of memory—look the same so that you need to remember only one interface? That’s the idea behind iostreams. They’re much easier, safer, and sometimes even more efficient than the assorted functions from the Standard C stdio library.
The iostreams classes are usually the first part of the C++ library that new C++ programmers learn to use. This chapter discusses how iostreams are an improvement over C’s stdio facilities and explores the behavior of file and string streams in addition to the standard console streams.
Why iostreams?
You might wonder what’s wrong with the good old C library. Why not "wrap" the C library in a class and be done with it? Indeed, this is the perfect thing to do in some situations. For example, suppose you want to make sure the file represented by a stdio FILE pointer is always safely opened and properly closed, without having to rely on the user to remember to call the close( ) function. The following program is such an attempt.
//: C04:FileClass.h
// stdio files wrapped
#ifndef FILECLASS_H
#define FILECLASS_H
#include <cstdio>
#include <stdexcept>
class FileClass {
std::FILE* f;
public:
struct FileClassError : std::runtime_error {
public:
FileClassError(const char* msg)
: std::runtime_error(msg) {}
};
FileClass(const char* fname, const char* mode = "r");
~FileClass();
std::FILE* fp();
};
#endif // FILECLASS_H ///:~
When you perform file I/O in C, you work with a naked pointer to a FILE struct, but this class wraps around the pointer and guarantees it is properly initialized and cleaned up using the constructor and destructor. The second constructor argument is the file mode, which defaults to "r" for "read.".
To fetch the value of the pointer to use in the file I/O functions, you use the fp( ) access function. Here are the member function definitions:.
//: C04:FileClass.cpp {O}
// FileClassImplementation
#include "FileClass.h"