#include "RefCountedString.h" #include // For stream << >> etc. #include // For C-style low-level string functions, e.g. toupper() and tolower(). #include "RefCountedStringRep.h" // For the RefCountedStringRep class details. using namespace std; /************************************************************************************************************ * Constructors, assignment, and destructor. ************************************************************************************************************/ // Default constructor, to hold brand new text (or no text). RefCountedString::RefCountedString(const char * text) { // Create a new representation object, to hold brand new text (for the first time). pRep = new RefCountedStringRep(text); } // Copy constructor, to share text with an existing RefCountedString. RefCountedString::RefCountedString(const RefCountedString & other) { // Just use the same representation object as the other RefCountedString. // This will just increment an internal ref count, rather than doing any actual copying. HookExistingRep(other.pRep); } // Assignment operator, to share text with a different existing RefCountedString. RefCountedString & RefCountedString::operator = (const RefCountedString & other) { // Guard against assignment-to-self. if (this != &other) { // Unkook from existing representation object (this will decrement its ref count). UnhookExistingRep(); // Then hook onto a different existing representation object (this will increment that one's ref count). HookExistingRep(other.pRep); } return *this; } // Destructor. RefCountedString::~RefCountedString() { // Just unhook ourself from our representation object. UnhookExistingRep(); } /************************************************************************************************************ * Modifier methods/operators. * In all these methods, if we are sharing our representation object with another RefCountedString object, * we must first create our own separate representation object that we can modify with impunity :-) ************************************************************************************************************/ // Convert our string to uppercase. RefCountedString & RefCountedString::ToUpperCase() { // Make sure we have a unique representation object. EnsureUniqueRep(); // Convert our text to uppercase. char * p = const_cast(pRep->actualString.c_str()); while (*p != '\0') { *p = toupper(*p); p++; } *p = '\0'; return *this; } // Convert our string to lowercase. RefCountedString & RefCountedString::ToLowerCase() { // Make sure we have a unique representation object. EnsureUniqueRep(); // Convert our text to lowercase. char * p = const_cast(pRep->actualString.c_str()); while (*p != '\0') { *p = tolower(*p); p++; } *p = '\0'; return *this; } // Append text onto end of our text. RefCountedString & RefCountedString::Append(const RefCountedString & other) { // Make sure we have a unique representation object. EnsureUniqueRep(); // Create a representation object holding appended string. pRep->actualString += other.pRep->actualString; return *this; } // Append text onto end of our text, using += syntax. RefCountedString & RefCountedString::operator +=(const RefCountedString & other) { return Append(other); } // Set character at specified index position. RefCountedString & RefCountedString::SetAt(int index, char ch) { // Make sure we have a unique representation object, which we can modify with impunity :-) EnsureUniqueRep(); // Set character at specified position. pRep->actualString[index] = ch; return *this; } // Input string content from stream. istream & RefCountedString::InputFromStream(istream & is) { // Make sure we have a unique representation object, which we can modify with impunity :-) EnsureUniqueRep(); // Input text into our RefCountedString object. is >> pRep->actualString; return is; } /************************************************************************************************************ * Const methods/operators * In all these methods, we can just use the existing representation object - we don't change its contents. ************************************************************************************************************/ // Get character at specified index position. char RefCountedString::GetAt(int index) const { return pRep->actualString.c_str()[index]; } // Indicate if RefCountedString is "false" (i.e. if it's an empty string). bool RefCountedString::operator !() const { return pRep->actualString.size() == 0; } bool RefCountedString::Equals(const RefCountedString & other) const { // The 1st test for equality is whether the RefCountedString objects share the same representation object. if (pRep == other.pRep) return true; // The 2nd test for equality is whether the RefCountedString objects happen to contain equal string objects. if (pRep->actualString == other.pRep->actualString) return true; // If we get here, the RefCountedString objects contain unequal string objects. return false; } // Output string content to stream. ostream & RefCountedString::OutputToStream(ostream & os) const { os << pRep->actualString << "[ref count " << pRep->refCount << "]"; return os; } /************************************************************************************************************ * Methods to handle the shareable representation object. ************************************************************************************************************/ // Hook ourself onto an existing representation object (and increment its ref count). void RefCountedString::HookExistingRep(RefCountedStringRep * pRep) { this->pRep = pRep; this->pRep->IncRefCount(); } // Unhook ourself from an existing representation object (and decrement its ref count, and dispose if no other refs). void RefCountedString::UnhookExistingRep() { pRep->DecRefCount(); if (pRep->HasNoUsers()) { delete pRep; pRep = 0; } } // Ensure we have a unique representation object, which no other RefCountedString object is sharing. void RefCountedString::EnsureUniqueRep() { // If no-one else is currently sharing our representation object, hooray! No need to do anything else! if (pRep->HasOneUser()) return; // Otherwise, create a new representation object for us only, and unhook the old one. RefCountedStringRep * pNewRep = new RefCountedStringRep(pRep->actualString.c_str()); UnhookExistingRep(); pRep = pNewRep; } /************************************************************************************************************ * Global binary operators (where the 1st parameter might not be a RefCountedString). ************************************************************************************************************/ // Determine whether two RefCountedString objects contain the same text. bool operator==(const RefCountedString & lhs, const RefCountedString & rhs) { return lhs.Equals(rhs); } // Determine whether two RefCountedString objects contain the same text. bool operator!=(const RefCountedString & lhs, const RefCountedString & rhs) { // You can always implement != as the inverse of ==. return !(lhs == rhs); } // Input a RefCountedString object from the console. istream & operator >> (istream & is, RefCountedString & str) { return str.InputFromStream(is); } // Output a RefCountedString object to the console. ostream & operator << (ostream & os, const RefCountedString & str) { return str.OutputToStream(os); }