Educational Objectives: After completing this assignment the student should have the following knowledge, ability, and skills:
Operational Objectives: Create a proper type AString whose objects can be used in place of C-strings in user programs. Overload the input and output operators for this type.
Deliverables: Two files astring.h and astring.cpp.
The goal for this assignment is to create a new type assignable string, AString, that is better behaved and easier for clients to use than low-level C-strings. A client program should be able to create an AString object of a desired length by a simple declaration (or call to operator new) with a parameter specifying how many characters the string object should hold. The client should be able to assign one AString object to another, return AString objects as values of functions, and pass AString objects to functions by value or reference. In addition, there should be a bracket operator that behaves like that for character arrays, except that it checks for out-of-bounds index errors by clients, and the AString member function Cstr() should facilitate the use of C string functions defined in string.h, provided the parameter type is const char*.
In essence, what is desired is that AString be a proper type while respecting the old assumption that strings are null-terminated. This latter feature causes a few compromises, but class AString should be fairly "bullet proof" at least as long as the client programmer is not intentionally mis-using the class.
The plan for implementing assignable strings is to maintain two private variables, one of type size_t and one of type char*. You choose the names for these variables in a sensible self-documenting manner. For this discussion, we will call them size_ and data_, respectively. The class has the responsibility for maintaining these variables in a consistent manner and for allocating and de-allocating resources (i.e., memory assigned to data_). In general, an AString object will have size_ + 1 bytes allocated and will maintain null-termination of data_, which leaves size_ elements of data_ where client characters can be stored. Constructors, copy constructor, and assignment operator should allocate memory appropriately and ensure that the C-string data_ is null-terminated. The destructor should de-allocate these resources.
Managing resources for client programs takes away the most aggravating and error-prone aspect of using C-strings. The implementation plan has other features that make client life even better, however: assignability and safety. Client programs can assign one AString object to another (this is part of being a proper type), and client attempts to access string elements with an out-of-bounds index are caught. This client mistake should result in an error message sent but not in program termination - just send a message through std::cerr and return data_[size_], the last element of the allocated memory (which should have the value '\0').
There are two methods Size() and Length() that return the size and length of the string represented by the AString object, respectively. Size() is the value stored in size_, whereas Length() is the number of characters in data_ before the first null character is encountered. Normally Size() and Length() would return the same number even though they are calculated in entirely different ways. However, a client could quite legally change an element of a string to '\0', effectively "shortening" the string and changing the value returned by Length().
The input and output operators should be overloaded and behave as clients have come to expect, and the client should be able to use an AString object in place of const char* parameters in library functions. For example, the client should be able to store a file name in an AString object filename and then open the file with a std::fstream object fs using the call fs.open(filename.Cstr()).
The overloaded input operator for a type T has this prototype:
std::istream& operator >> (std::istream& is, T& t);
and its implementation should always conform to these expectations:
Similarly, the overloaded output operator for a type T has this prototype:
std::ostream& operator << (std::ostream& os, const T& t);
and its implementation should output a token representation of the object of type T. The two I/O operators should be mutually consistent in the sense that
os << t;
followed by
is >> t;
on the token will result in reconstructing the object t as it was before output, and
is >> t;
followed by
os << t;
will result in the same token representation of a T object.
Create and work within a separate subdirectory cop3330/hw5. Review the COP 3330 rules found in Introduction/Work Rules.
Begin by copying the following files from the course directory /home/courses/cop3330p/fall08/ into your hw5 directory:
hw5/fastring.cpp submitscripts/hw5submit.sh area51/fastring_s.x area51/fastring_i.x
The naming of these files uses the convention that _s are compiled for Sun/Solaris and _i are compiled for Intel/Linux. Use one of the sample client executables to experiment to get an understanding of how your program should behave.
Create the class AString. Place the definition of the class and prototypes of overloaded operators in the file astring.h, and place the implementations of the class member functions and operators in the file astring.cpp.
Test your class thoroughly and correct any errors in functionality. A test clent program fastring.cpp is supplied.
Turn in two files astring.h and astring.cpp using the hw5submit.sh submit script.
Warning: Submit scripts do not work on the program and linprog servers. Use shell.cs.fsu.edu to submit projects. If you do not receive the second confirmation with the contents of your project, there has been a malfunction.
The type AString should be proper, that is, it should have constructors, destructor, copy constructor, and assignment operator that correctly manage resources (in this case, memory) for objects.
AString should have three constructors (plus the copy constructor):
AString should have these methods:
AString should have these methods that are versions of the bracket operator:
AString should have these operators overloaded:
The overloaded I/O operators should conform to the standard expectations expressed above for type T = AString.
Safety should be built in to the implementations:
Sample executables fastring_s.x and fastring_i.x may be copied from the area51 directory.
As always, you should read the test client source code for valuable insights into how the class is expected to work.
The best way to proceed with this kind of project is to develop the methods one at a time and test them as you go. This means building up a simple test client as you develop the class.
The correct implementation of the input operator is the most challenging component of this assignment. Note that this should not need friend status, so you have to use the API to get the string read into the string object. It is helpful to break the problem down into two stages:
Here is a "get" loop that skips clearspace characters in the istream is:
char x; do x = is.get(); while (!is.fail() && (x == ' ' || x == '\t' || x == '\n'));
This loop terminates with either is.fail() returning true or x being the first non-clearspace character in the stream.
When implementing the bracket operator for type AString, it is best to use pointer arithmetic and de-reference to access specific elements of data. If you use the bracket operator for data it can lead to confusion - which bracket operator is it, anyway?