Topical Information

The purpose of this quiz is to give you a chance to focus your knowledge of strings in C++.

Quiz Information

Questions

  1. 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.
    
    
  2. The string class contains the functions _____, _____, and _____ to search for substrings, characters not in another string, and characters in another string respectively.

    1. find, find_first_of, find_last_of NO
    2. find_first_not_of, find, find_first_of NO
    3. find_first_of, find_first_not_of, find_last_of NO
    4. find, find_first_not_of, find_first_of YES
    5. find_last_of, find_last_not_of, find NO
  3. 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.
  4. 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.

    1. one, zero, before, left, right NO
    2. one, one, in front of, right, right NO
    3. 1, 2, behind, right, left NO
    4. zero, zero, after, left, left YES
    5. 0, 1, left of, left, right NO
  5. 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.

  6. 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()!

  7. 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);
        }
    
    
  8. 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;
        }
    
    
  9. 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!)

    1.     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.
      
      
    2.     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!
      
      
    3.     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.
      
      
    4.     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.
      
      
    5.     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.
      
      
    6.     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.
      
      
  10. 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.
  11. 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:  ");
    
    
  12. 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.
    
    
  13. 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.