A String Class: Part II - Lab


The Problem

In a previous lab, we discussed some categories of problems that occur with strings represented as simple arrays of characters:


The Solution

Previously, we wrote a String class that addressed instances of two of those categories:

Problem Solution using a String class
The error of accessing characters outside a string Make the String's data private and only allow access to the characters of the string via a method that does range-checking.
The inefficient acquisition of a string's length Store the length of the string as an additional piece of data in the String class. Update that field every time the contents of the string is altered.

This time, we will augment our definition of the String class to address issues of static-ness, unnatural-ness and inconvenience.

Strings that Grow

We wish to allow our String class to accommodate strings of different sizes. To that end, instead of storing a string's characters in a fixed-size array:
char contents[MAX_STR_SIZE];

we will store them using a pointer:

char *contents;

and allocate space for the array it points to:

contents = new char[size];

With this design, we'll need to reallocate the array when its contents change. AND, we must remember to deallocate the memory once we are done with it.

Common Operations

We would like to use some of the basic operators with Strings. To do so, we will redefine methods that we wrote last time:

Access to Characters

Old way
char element(int i) const;

Usage:

if (str1.element(4) == 'a')
  ...
New way
char operator [](int i) const;

Usage:

if (str1[4] == 'a')
  ...

Comparison

Old way
int compare_to(const String &str) const;

Usage:

if (str1.compare_to(str2) >= 0)
  ...
New way
bool operator ==(const String &str) const;
bool operator !=(const String &str) const;
bool operator >(const String &str) const;
bool operator <(const String &str) const;
bool operator >=(const String &str) const;
bool operator <=(const String &str) const;

Usage:

if (str1 >= str2)
  ...

Assignment

Old way
void assign(const char *s);

Usage:

str1.assign(s);
New way
String &operator =(const String &str);

Usage:

str1 = str2;


Note: Note that we return a reference to a String. That's so that we can return the String object we assigned to. Why? Well, C++ allows things like:
int a, b, c;
...
(a = b) = c;  // Assign b to a, then c to a.
so we should allow the same for objects.

Replacing the Built-in Assignment Behavior

By default, we can assign one object to another of the same type. Why then, do we need to write our own assignment operator?

The default behavior for assignment is to assign each data member from one object to another. For objects containing a pointer, that doesn't always work as desired:

Code:
String str1("a");
String str2("xy");
str1 = str2;
Memory:
str1
------------   ------
|contents -+-->|a|\0|
|----------|   ------
|len      1|
------------

str2
------------   --------
|contents -+-->|x|y|\0|
|----------|   --------
|len      2|
------------
str1
------------
|contents -+---+
|----------|   |
|len      2|   |
------------   |
               |
str2           v
------------   --------
|contents -+-->|x|y|\0|
|----------|   --------
|len      2|
------------

For Strings, the result after the default assignment operation is that the contents data member (the one that points to where the characters are stored) points to the same location, so that if we change str1 or str2 we end up affecting the other!

To avoid this, we must write our own operator =, which does the right thing.


Copying

Requires that a copy of a String be made.

Like assignment, the default behavior for copying is to copy the object by copying each data member. Since our String class contains a pointer, we'll have to write our own copy constructor:

String(const String &str);


Note: Passing a String by reference, e.g.,
void foo(String &str);  // can change its String parameter
void bar(const String &str);  // just "uses" its String parameter
works just fine, since no copy is made. Returning a String from a function works fine as well (see the operator = we will write).

Conversion

It is reasonable to want to assign Strings from or compare Strings to both other String objects and arrays of characters:
String str1, str2;
char s[100];
...
str1 = str2;  // from a String object
str2 = s;     // from an array of characters

if (str1 == str2 && str1 < s)
  ...

It would be convenient to have methods that can deal with both. Rather than writing one method for each, e.g.:

bool operator ==(const String &str);

AND

bool operator ==(const char *s);

we can simply write a constructor that converts an array of characters into a String object:

String(const char *s);
and then write all other methods in terms of String objects. Then, if we call a String method with an array of characters, the compiler will first call the conversion constructor to convert it to a String object, and then pass that temporary String object to the method:
String str1;
char s[100];
...
if (str1 < s)  // Converts 's' to String, then
               // calls String::operator <
  ...


Your Task

You must write a String class that now addresses some aspect of all four categories of problems, and that provides some other basic String functionality.

Your new String class will have the following design (changes or additions from the previous version are in red):

  1. Modularity

    The String class code must be put in its own module, i.e., a file mystring.h (the header file) should hold the class definition and a file mystring.cpp (the implementation file) should hold the method definitions.

  2. Data

    Each String object should contain the following data as part of its internal representation:

  3. Functionality

    The String class should provide the following methods for setting up, accessing and changing strings:

In your implementation of String's methods, you may use the library functions provided by string.h, which operate on arrays of characters terminated by nul's (\0).


A Test Program

The file String-test2.cpp provides a main program to test your new String class.


Questions

In order to demonstrate an understanding of this String class, you should be able to determine which of the above methods are called in the following pieces of code:


BU CAS CS - A String Class: Part II - Lab
Copyright © 1993-2000 by Robert I. Pitts <rip at bu dot edu>. All Rights Reserved.