Using Classes Classes and Function Members Review We’ve seen that the iostream library provides the objects cin, cout and cerr: These objects were not originally provided in C++, but were added to the language using its class mechanism. Classes The C++ class mechanism allows a user to add new types to the language. Bell Labs’ Jerry Schwarz used this to create: • an istream class, to define the object cin; and • an ostream class, to define cout and cerr. The resulting I/O system was so elegant, it was incorporated into the language. Classes (ii) Another class that was added ‘after the fact’ is the string class, which provides a convenient way to store and operate on sequences of characters. The string library provides the type string, plus an assortment of useful string-processing operations. String Objects Objects of type string are indexed variables, meaning that each character in the variable can be accessed via an index or subscript: string name = “John Q. Doe”; name J o h n 0 1 2 3 4 Q . 5 6 7 D o e 8 9 10 Use the subscript operator to access individual chars: char firstInitial = name[0]; // firstInitial == ‘J’ Dynamic string Objects Objects of type string can grow and shrink as necessary to store their contents: string name = “John Q. Doe”; name J o h n 0 1 2 3 4 // name.size() == 11 Q . 5 6 name = “Philleas Fogg”; name 7 D o e 8 9 10 // name.size() == 13 P h i l l e a s 0 1 2 3 4 5 6 7 8 F o g g 9 10 11 12 Some string Operations Operation read a word from an istream read a line from an istream find the length of the string find if a string is empty access the char at index i concatenate two strings access a substring of a string insert a substring into a string remove a substring find a substring in a string compare two strings ... string function istream >> str; getline(istream, str); str.size() str.empty() str[i] str1 + str2 str.substr(Pos, NumChars) str.insert(Pos, SubStr); str.remove(Pos, NumChars); str.find(Pattern, StartPos) str1 == str2 (or !=, <, >, <=, >=) Discussion Some string operations are “normal” functions: getline(cin, aString); Other string operations are function members: aString.size(); Function members are “messages” that class objects “understand” and to which they respond... For example, aString “knows” how big it is, so when it receives the size() message, it responds with the appropriate answer. Function Members Where a “normal” function is an external agent that acts upon an object, a function member is a message that elicits an internal response from the object receiving it. Examples: getline(cin, aString); // getline() acts on aString if (aString.empty()) // ask aString, “are you empty?” if (cin.good()) // ask cin, “are you good?” In this sense, class objects are “more intelligent” than regular char, int, double, ... objects. Classes Most classes provide a rich set of operations that can be used to manipulate objects of the class. To use a class effectively, you must know what kinds of functions (“normal” and member) are available to operate on objects of that class. Otherwise, you risk spending much of your time “reinventing the wheel.” Example Suppose a problem requires me to find the position of a particular character within a string. I can either write/call my own function to do so... int PositionOf(char ch; string str) { for (int i = 0; i < str.size(); i++) if (str[i] == ch) return i; } return -1; ... int pos = PositionOf(myChar, aString); Example (ii) ... or I can ask aString to locate myChar for me, using the string function member named find(): int pos = aString.find(myChar, 0); Which is less work? • Writing your own is more work to code, test and debug, and the result is less flexible than find(). • Using find() requires awareness (i) that the function member exists, and (ii) of how to use it. Discussion Be aware of the functionality a class provides (but don’t memorize the nitty-gritty details). Know where (in a reference book) to look up the operations a class supports. Then, when a problem involves an operation on a class object, scan the list of operations looking for one that you can use -- don’t reinvent the wheel! Example The text provides a RandomInt class. Objects of this class are integers with “random” values, which can be used to simulate all sorts of “random” occurrences. #include “RandomInt.h” ... RandomInt die1(1,6), die2(1,6); // two dice die1.Generate(); die2.Generate() // roll the dice cout << “dice roll = “ << die1 + die2 << endl; // display results RandomInt Objects The range of random values is specified when an object is declared: #include “RandomInt.h” ... const int HEADS = 0, TAILS = 1; RandomInt coin(HEADS,TAILS); coin.Generate(); // flip coin cout << coin << endl; // display result RandomInt Operations Operation RandomInt function Display a RandomInt ostream << randInt Declare a RandomInt RandomInt name; Declare a RandomInt within range first..last RandomInt name(first, last); Generate new random value randInt.Generate(); Generate new random value from range first..last randInt.Generate(first, last); Add two RandomInt values randInt1 + randInt2 (also -, *, /) Compare two RandomInt values randInt1 == randInt2 (also !=, <, >, <=, >=) Sample Program #include <iostream> // cin, cout using namespace std; #include “RandomInt.h” // RandomInt class int main() { const int HEADS = 0, TAILS = 1; // for readability RandomInt coin(HEADS, TAILS); // model a coin int numHeads = 0, numTails = 0; // counters for (int i = 1; i <= 10000; i++) // loop 10,000 times { coin.Generate(); // flip coin if (coin == HEADS) // count numHeads++; // heads else // vs. tails numTails++; } cout << “\nIn 10,000 tosses of a coin,\n” << “ heads occurred “ << numHeads << “ times\n” << “ and tails occurred << numTails << “ times” << endl; } Other Classes As mentioned earlier, cin and cout are objects of the istream and ostream classes, respectively. To use these classes effectively, you must be aware of the range operations available for them... Some istream Operations istream function cin >> ch; cin.get(ch); cin.good() cin.bad() cin.fail() cin.clear(); cin.ignore(n, ch); Description Extract next non-whitespace character from cin and store it in ch. Tell cin, “Put your next character (whitespace or not) into ch.” Ask cin, “Are you in good shape?” Ask cin, “Is something wrong?" Ask cin, “Did the last operation fail?” Tell cin, “Reset yourself to be good.” Tell cin, ignore the next n characters, or until ch occurs, whichever comes first. Some ostream Operations ostream function cout >> expr cout.put(ch); cout << flush cout << endl cout << fixed cout << scientific cout << showpoint Description Insert expr into cout. Tell cin, “Insert ch into yourself.” Write contents of cout to screen. Write a newline to cout and flush it. Display reals in fixed-point notation. Display reals in scientific notation. Display decimal point and trailing zeros for real whole numbers. cout << noshowpoint Hide decimal point and trailing zeros for real whole numbers. More ostream Operations ostream function Description cout << showpos Display sign for positive values. cout << noshowpos Hide sign for positive values. cout << boolalpha Display true, false as “true”, “false”. cout << noboolalpha Display true, false as 1, 0. cout << setprecision(n) Display n decimal places for reals. cout << setw(w) Display next value in field width w. cout << left Left-justify subsequent values. cout << right Right-justify subsequent values. cout << setfill(ch) Fill leading/trailing blanks with ch. Summary Well-designed classes provide a rich set of operations that make them useful for many problems. Operations can be external (normal functions), or internal (function members) to the class. Function members act as messages to class objects. To use a class effectively, you must know • what capabilities the class provides; and • how to use those capabilities.