C++ has a string class that behaves very much like the Java String class (but notice that the C++ string class name begins with a lowercase letter). Introducing the string class before exploring classes in general may be a little confusing. However, string objects are an important data type. Presenting them now will allow us to use them as class members when we formally study classes in the next chapter, making our classes more realistic and useful. The early introduction relies on your experience with classes and methods from Java in CS 1400. Furthermore, we'll restrict our treatment of the string class to basic operations and terminology.
Both C++ and Java classes provide one significant advantage over C-strings: once created, a C-string has a maximum length beyond which it cannot grow. The size of the character array storing the C-string limits the C-string's length. Alternatively, instances of both string classes are dynamic and can grow when needed. The ability to grow suggests that string objects can manage their memory. However, in the case of Java, saying that a string can grow is a bit of an oversimplification as Java strings are immutable - they can't change. So, any operation that looks like it modifies a Java string actually creates a new one. Alternatively, When a C++ string needs to grow, it allocates a new, larger array with new
, copies the existing data into the new array, and then deallocates the old array with delete
. Therefore, growing an already large string can slow a program due to the data copy.
#include <string> |
#include <iostream> #include <string> using namespace std; |
The string class defines ten constructor functions. Programs automatically call constructors whenever they create or instantiate a new object. Constructors can be complex, but often they only initialize the new object. The following table describes three of the most frequently used constructors.
Prototype | Example | Comments |
---|---|---|
string(); |
string s1; |
Default constructor: builds an empty string |
string(const char* s); |
string s2("Hello, World!"); |
Conversion constructor: converts a C-string into a string |
string(const string& s); |
string s3(s2); |
Copy constructor: makes a new string by copying a string |
One of the many benefits classes provide programmers is hiding the complexity of the class's operations. They do this by separating the operation's interface from its implementation. So, programmers see what the class can do (the function prototypes) but not how it does it (the function bodies). Nevertheless, we can gain some insight into how the string constructors behave by examining the results of a simple program:
#include <iostream> #include <string> using namespace std; int main() { string s1; // default constructor string s2("hello"); // conversion constructor string s3(s2); // copy constructor string s4("the quick brown fox jumps over the lazy dog"); cout << s1.size() << " " << s1.length() << " " << s1.capacity() << endl; cout << s2.size() << " " << s2.length() << " " << s2.capacity() << endl; cout << s3.size() << " " << s3.length() << " " << s3.capacity() << endl; cout << s4.size() << " " << s4.length() << " " << s4.capacity() << endl; return 0; }(a)
0 0 15 5 5 15 5 5 15 43 43 47(b)
size
and length
are synonyms - they are different names for the same operation, which is getting the number of characters currently stored in a string object. The string member function capacity
gets the number of characters the string can store before it must grow.size ≤ capacity
.
C++ classes can give new meanings to existing operators, but the new meanings only apply when at least one operand is an instance of the defining class. Assigning a new meaning to an existing operator is called overloading and is the subject of chapter 10.
Operator | Meaning | Example |
---|---|---|
= |
Assignment Converts a C-string to a string |
s1 = s2; s = "Hello, World!" |
+ |
Concatenation | s = s1 + s2; |
+= |
Concatenation with assignment | s += s2 |
== |
Equality | if (s1 == s2) . . . |
!= |
Inequality | if (s1 != s2) . . . |
<, <=, >, >= |
Relational | if (s1 < s2) . . . |
[] and at() |
Character access | char c = s1[i]; |
<< and >> |
Input and output | cout << s1; cin >> s1; |
at()
is a function and not an operator. If s is an instance of the string class, then both s.at(i)
and [i]
reference the i-th character in string s, but the at
function verifies that 0 ≤ i < s.length() while the index operator, []
, does not.>>
, works for very simple strings, it does not work for strings that contain spaces or tab characters. The next section presents a more reliable way of entering strings.#include <iostream> #include <string> using namespace std; int main() { string s1 = "Hello"; string s2 = "World"; s1 += ", "; // concatenation with assignment s2 += "!"; // concatenation with assignment if (s1 < s2) cout << s1 << endl; else cout << s2 << endl; if (s1 == s2) cout << "They are ==" << endl; else cout << "They are !=" << endl; if (s1 != s2) cout << "They are !=" << endl; else cout << "They are ==" << endl; string s3 = s1 + s2; // assignment and concatenation for (size_t i = 0; i < s3.length(); i++) cout << s3[i]; // no bounds checking cout << endl; for (size_t i = 0; i < s3.length(); i++) cout << s3.at(i); // bounds checking return 0; }(a)
Hello, They are != They are != Hello, World! Hello, World!(b)