Previous Section  < Free Open Study >  Next Section

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

}

    Previous Section  < Free Open Study >  Next Section