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); } |