Case Study: String Class
Case Study String Class
As a capstone exercise to our study of overloading, we will build our own String class to handle the creation and manipulation of strings (Figs. 11.911.11). The C++ standard library provides a similar, more robust class string as well. We present an example of the standard class string in Section 11.13 and study class string in detail in Chapter 18. For now, we will make extensive use of operator overloading to craft our own class String.
Figure 11.9. String class definition with operator overloading.
(This item is displayed on pages 595 - 596 in the print version)
1 // Fig. 11.9: String.h 2 // String class definition. 3 #ifndef STRING_H 4 #define STRING_H 5 6 #include 7 using std::ostream; 8 using std::istream; 9 10 class String 11 { 12 friend ostream &operator<<( ostream &, const String & ); 13 friend istream &operator>>( istream &, String & ); 14 public: 15 String( const char * = "" ); // conversion/default constructor 16 String( const String & ); // copy constructor 17 ~String(); // destructor 18 19 const String &operator=( const String & ); // assignment operator 20 const String &operator+=( const String & ); // concatenation operator 21 22 bool operator!() const; // is String empty? 23 bool operator==( const String & ) const; // test s1 == s2 24 bool operator<( const String & ) const; // test s1 < s2 25 26 // test s1 != s2 27 bool operator!=( const String &right ) const 28 { 29 return !( *this == right ); 30 } // end function operator!= 31 32 // test s1 > s2 33 bool operator>( const String &right ) const 34 { 35 return right < *this; 36 } // end function operator> 37 38 // test s1 <= s2 39 bool operator<=( const String &right ) const 40 { 41 return !( right < *this ); 42 } // end function operator <= 43 44 // test s1 >= s2 45 bool operator>=( const String &right ) const 46 { 47 return !( *this < right ); 48 } // end function operator>= 49 50 char &operator[]( int ); // subscript operator (modifiable lvalue) 51 char operator[]( int ) const; // subscript operator (rvalue) 52 String operator()( int, int = 0 ) const; // return a substring 53 int getLength() const; // return string length 54 private: 55 int length; // string length (not counting null terminator) 56 char *sPtr; // pointer to start of pointer-based string 57 58 void setString( const char * ); // utility function 59 }; // end class String 60 61 #endif |
First, we present the header file for class String. We discuss the private data used to represent String objects. Then we walk through the class's public interface, discussing each of the services the class provides. We discuss the member-function definitions for the class String. For each of the overloaded operator functions, we show the code in the program that invokes the overloaded operator function, and we provide an explanation of how the overloaded operator function works.
String Class Definition
Now let us walk through the String class header file in Fig. 11.9. We begin with the internal pointer-based representation of a String. Lines 5556 declare the private data members of the class. Our String class has a length field, which represents the number of characters in the string, not including the null character at the end, and has a pointer sPtr that points to the dynamically allocated memory representing the character string.
Overloading the Stream Insertion and Stream Extraction Operators as friends
Lines 1213 (Fig. 11.9) declare the overloaded stream insertion operator function operator<< (defined in Fig. 11.10, lines 170174) and the overloaded stream extraction operator function operator>> (defined in Fig. 11.10, lines 177183) as friends of the class. The implementation of operator<< is straightforward. Note that operator>> restricts the total number of characters that can be read into array temp to 99 with setw (line 180); the 100th position is reserved for the string's terminating null character. [ Note: We did not have this restriction for operator>> in class Array (Figs. 11.611.7), because that class's operator>> read one array element at a time and stopped reading values when the end of the array was reached. Object cin does not know how to do this by default for input of character arrays.] Also, note the use of operator= (line 181) to assign the C-style string temp to the String object to which s refers. This statement invokes the conversion constructor to create a temporary String object containing the C-style string; the temporary String is then assigned to s. We could eliminate the overhead of creating the temporary String object here by providing another overloaded assignment operator that receives a parameter of type const char *.
Figure 11.10. String class member-function and friend-function definitions.
(This item is displayed on pages 597 - 600 in the print version)
1 // Fig. 11.10: String.cpp 2 // Member-function definitions for class String. 3 #include 4 using std::cerr; 5 using std::cout; 6 using std::endl; 7 8 #include 9 using std::setw; 10 11 #include // strcpy and strcat prototypes 12 using std::strcmp; 13 using std::strcpy; 14 using std::strcat; 15 16 #include // exit prototype 17 using std::exit; 18 19 #include "String.h" // String class definition 20 21 // conversion (and default) constructor converts char * to String 22 String::String( const char *s ) 23 : length( ( s != 0 ) ? strlen( s ) : 0 ) 24 { 25 cout << "Conversion (and default) constructor: " << s << endl; 26 setString( s ); // call utility function 27 } // end String conversion constructor 28 29 // copy constructor 30 String::String( const String © ) 31 : length( copy.length ) 32 { 33 cout << "Copy constructor: " << copy.sPtr << endl; 34 setString( copy.sPtr ); // call utility function 35 } // end String copy constructor 36 37 // Destructor 38 String::~String() 39 { 40 cout << "Destructor: " << sPtr << endl; 41 delete [] sPtr; // release pointer-based string memory 42 } // end ~String destructor 43 44 // overloaded = operator; avoids self assignment 45 const String &String::operator=( const String &right ) 46 { 47 cout << "operator= called" << endl; 48 49 if ( &right != this ) // avoid self assignment 50 { 51 delete [] sPtr; // prevents memory leak 52 length = right.length; // new String length 53 setString( right.sPtr ); // call utility function 54 } // end if 55 else 56 cout << "Attempted assignment of a String to itself" << endl; 57 58 return *this; // enables cascaded assignments 59 } // end function operator= 60 61 // concatenate right operand to this object and store in this object 62 const String &String::operator+=( const String &right ) 63 { 64 size_t newLength = length + right.length; // new length 65 char *tempPtr = new char[ newLength + 1 ]; // create memory 66 67 strcpy( tempPtr, sPtr ); // copy sPtr 68 strcpy( tempPtr + length, right.sPtr ); // copy right.sPtr 69 70 delete [] sPtr; // reclaim old space 71 sPtr = tempPtr; // assign new array to sPtr 72 length = newLength; // assign new length to length 73 return *this; // enables cascaded calls 74 } // end function operator+= 75 76 // is this String empty? 77 bool String::operator!() const 78 { 79 return length == 0; 80 } // end function operator! 81 82 // Is this String equal to right String? 83 bool String::operator==( const String &right ) const 84 { 85 return strcmp( sPtr, right.sPtr ) == 0; 86 } // end function operator== 87 88 // Is this String less than right String? 89 bool String::operator<( const String &right ) const 90 { 91 return strcmp( sPtr, right.sPtr ) < 0; 92 } // end function operator< 93 94 // return reference to character in String as a modifiable lvalue 95 char &String::operator[]( int subscript ) 96 { 97 // test for subscript out of range 98 if ( subscript < 0 || subscript >= length ) 99 { 100 cerr << "Error: Subscript " << subscript 101 << " out of range" << endl; 102 exit( 1 ); // terminate program 103 } // end if 104 105 return sPtr[ subscript ]; // non-const return; modifiable lvalue 106 } // end function operator[] 107 108 // return reference to character in String as rvalue 109 char String::operator[]( int subscript ) const 110 { 111 // test for subscript out of range 112 if ( subscript < 0 || subscript >= length ) 113 { 114 cerr << "Error: Subscript " << subscript 115 << " out of range" << endl; 116 exit( 1 ); // terminate program 117 } // end if 118 119 return sPtr[ subscript ]; // returns copy of this element 120 } // end function operator[] 121 122 // return a substring beginning at index and of length subLength 123 String String::operator()( int index, int subLength ) const 124 { 125 // if index is out of range or substring length < 0, 126 // return an empty String object 127 if ( index < 0 || index >= length || subLength < 0 ) 128 return ""; // converted to a String object automatically 129 130 // determine length of substring 131 int len; 132 133 if ( ( subLength == 0 ) || ( index + subLength > length ) ) 134 len = length - index; 135 else 136 len = subLength; 137 138 // allocate temporary array for substring and 139 // terminating null character 140 char *tempPtr = new char[ len + 1 ]; 141 142 // copy substring into char array and terminate string 143 strncpy( tempPtr, &sPtr[ index ], len ); 144 tempPtr[ len ] = ' |