Selected C++ Example #19
// Chapter 9 Example #1
// This C++ example illustrates a minimal public interface
// implementation for a reference-counted String class. The
// minimal interface used is the one described in Chapter 9
// of this text. The code is actually three different files,
// string.hxx (the header file) , string.in1 (the inline
// function file included into the header file), and string.cpp
// (the C++ methods file). The three files are provided to
// illustrate the code organization as well as the details of
// the interface. Extra methods/operators are shown in the
// class definition but are not provided in the source code
// (for brevity).
// String.hxx
// This file contains the class definition for the class String.
#ifndef _STRING_
#define _STRING_
#include <iostream.h>
#include <string.h>
// The external definition of the global string that contains the
// name of this class 's name, i.e. , String.
extern char* String_type;
class String {
struct StringRep {
char* s;
int ref_cnt;
StringRep(const char*);
};
StringRep *str;
void disconnect_self();
public:
// Constructors and Destructors
String(const char*);
String(const String&);
~String();
// Required functions for each class
const char* type() const;
String& shallow_copy(const String&);
String& deep_copy(const String&);
String* shallow_copy()const;
String* deep_copy()const;
int equal(const String&) const;
int same(const String&) const;
// Additional member functions
String& upper_case();
String& lower_case();
String& upper_case(const String&);
String& lower_case(const String&);
String& reverse();
intlength()const;
//Required operators
String&operator=(const String&);
int operator==(const String&) const;
int operator!=(const String&) const;
friend ostream& operator<<(ostream&, const String&);
friend istream& operator>>(istream&, String&);
// Additional operators
String& operator=(const char*);
String operator+(const String&)const;
String& operator+=(const String&);
int operator<(const String&) const;
int operator>(const String&) const;
int operator<=(const String&) const;
int operator>=(const String&) const;
String operator~() const;
char& operator[](intindex) const;
// Required self-test function.
static void test();
};
#include ''string.inl''
#endif
// String.inl
// This file contains all the inline function definitions used by
// the class String.
// The function that returns the type of the class String is
// implemented as an inline function, which returns the global
// type string of this class. This global variable contains the
// constant String, is defined in String.cxx, and is used to
// facilitate fast testing of the type of an object. For example,
// if (o.type() == String_type) {
// as opposed to
// if (!strcmp(o.type(), ''String'')) {
inline const char*
String::type() const
{
return(String_type);
}
// The constructor for String, which takes a character pointer as
// an argument, creates a new StringRep structure on the heap
// and allocates space within it for the String' s characters.
// These characters are copied into the allocated space, and the
// reference counter is assigned to one.
inline
String::String(const char* s)
{
str = new StringRep(s);
}
// The destructor for the String class must disconnect the String
// object from its StringRep implementation. The disconnect_self
// function is a private function that decrements the reference
// counter and, if it goes to zero, cleans up the object.
inline
String::~String()
{
disconnect_self();
}
// The shallow copy function for the String class, which takes
// zero arguments, is implemented as an inline function call to
// the copy constructor for Strings. The object that calls this
// constructor is allocated space on the heap. The source of the
// copy is the current object, which called the shallow copy
// function in the first place.
inline String*
String::shallow_copy() const
{
return(new String(*this));
}
// The deep copy function for the String class, which takes zero
// arguments, is implemented as an inline function call to the
// constructor for Strings that takes a character pointer as an
// argument. This heap object encapsulates the character array
// in the current object (i.e., *this).
inline String*
String::deep_copy() const
{
return(new String(str->s));
}
// The equal function for the String class is implemented as an
// inline function call to the standard C library function
// ''strcmp,'' which tests the equality of two strings
inline int
String::equal(const String& rhs) const
{
return(!strcmp(str->s, rhs.str->s));
}
// The ''same'' function for the String class is implemented as an
// inline function that tests the StringRep pointers to ensure
// that they are the same. If they are the same, then the two
// String objects are the exact same object or shallow copies of
// each other.
inline int
String::same(const String& rhs) const
{
return(str == rhs.str);
}
// The overloaded equivalence operator is implemented as an
// inline function call to the equal function defined on the
// class String. This function returns one if the two strings are
// equivalent (i.e., contain the same characters) and zero
// otherwise. For testing exact equality, see the function
// ''same()'' above.
inline int
String::operator==(const String& rhs) const
{
return(equal(rhs));
}
// The overloaded nonequivalence operator is implemented as
// an inline function call to the equal function defined on the
// class String. The return value of the equal function is
// inverted before being returned. This operator returns one if
// the strings are not equal and zero otherwise.
inline int
String::operator!=(const Strings rhs) const
{
return(!equal(rhs));
}
// String.cxx
// This file contains the member functions for the class String.
#include ''string.hxx''
char* String_type= ''String'';
// The constructor for StringRep is used as a support function in
// several of the member functions for Strings. It takes a
// character pointer as an argument, allocates space for it,
// copies the characters into this allocated space, and assigns
// its reference count to one.
String::StringRep::StringRep(const char* old_s)
{
if (old_s != NULL) {
s = new char[strlen(old_s)+1];
strcpy(s, old_s);
}
else {
s = NULL;
}
ref_cnt = 1;
}
// The disconnect_self function is a private function that will
// separate a String object from its internal StringRep node.
// This process involves decrementing the reference counter in
// the StringRep node and, if it is zero (indicating that the
// String disconnecting itself is the last object pointing to the
// StringRep), the String object cleans up the StringRep object.
void
String::disconnect_self()
{
if (--str->ref_cnt == 0) {
delete str->s;
delete str;
}
}
// The copy constructor for the String class will increment the
// ref_cnt field of the String on the right-hand side of the
//initialization, i.e., the argument rhs. The StringRep pointer
// of the new object is then initialized to point at the existing
// StringRep (to which rhs is also pointing).
String::String(const String& rhs)
{
rhs.str->ref_cnt++;
str = rhs.str;
}
// The shallow copy function for Strings will simply assign the
// pointer to the StringRep object to point at the StringRep of
// the object on the right-hand side. The String object on the
// left-hand side of the function call (i.e., this) must first
// disconnect itself from its StringRep.
String&
String::shallow_copy(const String& rhs)
{
disconnect_self();
str = rhs.str;
str->ref_cnt++;
return(*this);
}
// The deep copy function for the String class (which takes an
// additional argument) first disconnects the node from its
// current StringRep and then creates a new StringRep,
// assigning that StringRep to the value of the character array
// stored in the StringRep of the rhs String object. This
// function returns a reference to the String object on the left-
// hand side (i.e., *this) to facilitate nested function calls.
String&
String::deep_copy(const String& rhs)
{
if (!same(rhs)){
disconnect_self();
str = new StringRep(rhs.str->s);
}
return(*this);
}
// The overloaded operator= function for the String class must
// first disconnect the String object on the left-hand side from
// its associated StringRep. It then assigns the StringRep pointer
// in that object to point at the StringRep object in the String
// object on the right-hand side of the assignment operator.
// This function returns a reference to the String object on the
// right-hand side in order to facilitate nested function calls.
String&
String::operator=(const String& rhs)
{
if (!same(rhs)) {
disconnect_self();
str = rhs.str;
str->ref_cnt++;
}
return(*this);
}
// The overloaded assignment operator for the String class, which
// takes a character pointer as an argument, first disconnects
// the string from its StringRep data member. It then creates a
// new StringRep structure and encapsulates the argument array
// of characters (i.e., rhs) into it.
String&
String::operator=(const char* rhs)
{
disconnect_self();
str = new StringRep(rhs);
return(*this);
}
// The overloaded input and output operators for Strings simply
// read in the character array using standard C++ functions.
// The overloaded input operator will first disconnect the existing
// String from its associated StringRep object.
ostream&
operator<<(ostream& o, const String& rhs)
{
o << rhs.str->s;
return(o);
}
istream&
operator>>(istream&i, String& rhs)
{
char buf[512];
rhs.disconnect_self();
i >> buf;
rhs.str = new String::StringRep(buf);
return(i);
}
|