The purpose of this quiz is to give you a chance to focus your knowledge of strings in C++.
What does concatenation mean? What operation(s) are used to concatenate strings? Can we concatenate two literal strings? What about two literal characters?
Concatenation is attaching one thing to the end of another. We use the + operator to concatenate two string objects, a string object and a literal string, or a string object and a char. We can also use the += operator to concatenate such pairs and change the content of the left-hand string object to the result of the concatenation. Neither two literal strings nor two literal chars can be concatenated. Neither can any combination of the two.
The string class contains the functions _____, _____, and _____ to search for substrings, characters not in another string, and characters in another string respectively.
TRUE ✓ | FALSE ✗ | Many actions on strings can be done all at once (like input, output, assignment, concatenation, etc.). | ||
---|---|---|---|---|
TRUE ✓ | FALSE ✗ | But when we want to do something novel (capitalization, for instance), we must use a loop to process each character individually. | ||
TRUE ✓ | FALSE ✗ | To do this individual processing, we can use a for loop controlled by an integer/string::size_type index which will stop before we reach the string's length/size. |
The integral-style values used to indicate positions in a string count from _____. Likewise, the values used with functions like insert, replace, and erase to indicate position and number are _____-based. We should use care not to mis-count here or we could insert _____ the character we wanted to insert before, erase the wrong set of characters (one _____ of where we wanted), or replace a set of characters that is 'shifted' _____ of the ones we wanted to replace.
The string::assign() function will overwrite the 'calling' string with the specified sub-string of the argument string. This could also be accomplished by using the = (assignment) operator with a call to the substr method on the right side.
Similarly, the string::clear() function could be substituted with a call to erase starting at 0 (zero) and ending at string::size(). We could also use the string::erase() function and pass it 0 and the result of string::length() as its two arguments.
This seeming redundancy in the string class design gives the programmer wanting to use strings the flexibility to perform an action the way she sees it being done. The 'redundant' functions are most likely written in terms of one another so that there isn't really any redundancy in the code after all.
Although the getline() function is great when we want to read a string that contains spacing, it does have a slight drawback. When it is called immediately following an >> (extraction) operation ('immediate' with respect to cin's view of the world, that is), it will input an empty string — hardly what the user wanted to give us.
To fix this problem, we should do several things before we call getline(). First we must flush cout. This is because our next action will be to peek cin — and some library implementors fail to have cout print prompts if they aren't really 'reading' input. If we see a newline character, we know that we'll fall into the trap outlined above. When this happens, we call cin.ignore to effectively skip over that character. This should give us a fresh start for our call to getline()!
Show code that will locate any occurrences of Robert in a string and substitute Bob for them. (It seems that Robert is coming off as a 'stuffed-shirt' around the office and needs to look more common and friendly.)
string letter; // fill with content string Robert_at; // where is "Robert" located? Robert_at = letter.find("Robert"); while (Robert_at != string::npos) { letter.replace(Robert_at, 6, "Bob"); Robert_at = letter.find("Robert", Robert_at+3); }
Could you have the substitution alternate between Bob and Rob? (This will make Robert seem more approachable, too. *grin*) Try that here:
string letter; // fill with content string Robert_at; // where is "Robert" located? bool toggle{false}; // flip back and forth Robert_at = letter.find("Robert"); while (Robert_at != string::npos) { toggle = !toggle; letter.replace(Robert_at, 6, toggle ? "Bob" : "Rob"); Robert_at = letter.find("Robert", Robert_at+3); }
Write a function which can change a (single) string to all uppercase.
string toupper(string & s) { for (char & c : s) { c = static_cast<char>(toupper(c)); } return s; }
Explain what is wrong (if anything) in each of the following code fragments. (Note: These are fragments! You may assume everything around the fragment is working fine. If anything is wrong, it will be in the lines presented!)
string s; cout << "Enter string: "; cin >> s; s = toupper(s); cout << "\n\nThat's '" << s << "', right?\n";
This assumes that toupper will work for strings which is pattently not the case (pardon the pun). toupper, as provided by the standard library, only works for the char data type.
string s = "Hello"; s[5] = '!'; cout << s << endl;
s is defined to have 5 characters in it and then the sixth position is accessed. This position is technically not a proper position in the string object. Technically, read access to it will return the null character ('\0'), but write access like here is not a good idea!
if ("hello" == "Hello") { cout << "The world's gone mad!!!\a\n"; } else { cout << "Oh, well, then that's all right, then...\n"; }
This tries to compare two literal strings instead of a literal and a string object or two string objects. Only these other comparisons can work. Comparing two literal strings will result in gibberish rather than a proper answer.
while (s.find("howdy") == string::npos) { s.replace(s.find("howdy"), 5, "hi"); }
There are two things wrong here. The first is that the == operation should be a !=. This is because we are otherwise trying to replace something that was NOT found. The second issue is that we are calling for finding "howdy" twice in a row. We should cache the result for efficiency, of course. This would also give us the opportunity to make the second and later calls more efficient by using the defaulted second argument to find to move past the replaced area.
string s; char t; cout << "Enter 'A': "; cin >> t; cout << "Enter a line of text: "; getline(cin, s);
Here we have a getline following an extraction (>>). This will result in an empty string object instead of the desired line of text. We can simply use an ignore on cin to remove the unwanted newline — and possibly other stuff — after the extraction. That will make the buffer clean before any other operations need it.
string s, t = ""; getline(cin, s); while (!s.empty()) { t += '\n' + s; getline(cin, s); } s.assign(t, 1, t.length()); s += '\n';
While not technically a problem, this code is wasteful in always
putting on a newline and then effectively erasing it at the end
of the loop. A simple one-way branch could detect that t was
initially empty and not add the newline that time around.
TRUE ✓ | FALSE ✗ | Having a function take string arguments can extend its usefulness to many new situations. | ||
---|---|---|---|---|
TRUE ✓ | FALSE ✗ | Such string arguments are often made 'fast-pass-by-value' (which means they must be modified with the keyword const and the symbol &). | ||
TRUE ✓ | FALSE ✗ | The actual arguments passed to these functions can be either string variables, string constants, or even literal strings. |
The following function is terribly program specific:
double input(void) { double pay; cout << "Enter your pay rate: "; cin >> pay; return pay; }
To fix it, we would normally do something like this:
// caller must prompt before calling us!!! double input(void) { double x; cin >> x; return x; }
But with strings, we can now print the prompt from the function and still have the function be completely generic — letting the caller tell us what the prompt should be on each call. Show how this can be done:
double input(const string & prompt) { double x; cout << prompt; cin >> x; return x; }
Show two or more calls to your new input function to demonstrate its genericity and utility.
pay = input("Enter your pay rate: "); hours = input("Enter your number of hours worked: ");
A common security breech is caused by entering more text than a prompt can handle. This allows the attacker to corrupt memory in such a way as to gain 'super-user' or 'administrator' access privileges. Is it possible for this to happen with the string class used to read in text? Why/Why not?
This is not possible to do with a string object input because the string will grow as needed to add the excess characters. On the other hand, this could cause a sort of manual denial of service attack that would take down the machine when the limits of memory were reached.
When we need to have whole words or lines as data, the string class is great. But when we just need a yes/no or single digit/letter menu response, we still use char. Briefly explain why this is so. (There are multiple reasons. Tell all you can muster.)
First, the string would take up more memory than needed for such simple responses. Second, it would take time to allocate the space for the string's content and copy it from the cin buffer to the new space. Third, we can easily handle the possibility of excess wordiness from a user with a simple use of ignore after the extraction of the char.
What do we do when the user types a word or line when we just wanted a single char? Won't that cause input stream problems later on?
We can use ignore on cin to get rid of the excess text like so: cin.ignore(numeric_limits<streamsize>::max(), '\n'); This will throw out the entire rest of the input line no matter how much stuff is on it. The maximum value of the streamsize data type acts as a flag value to ignore so that it isn't just an exact number of characters to throw away but a sort of infinity mark.