430 likes | 608 Views
C String, Proper Type, and String Objects . Andy Wang COP 4530: Data Structures, Algorithms, and Generic Programming. Lecture Overview. Goal Design a string class that overcomes the disadvantages of C strings Roadmap Critique of C strings The need for proper type
E N D
C String, Proper Type, and String Objects Andy Wang COP 4530: Data Structures, Algorithms, and Generic Programming
Lecture Overview • Goal • Design a string class that overcomes the disadvantages of C strings • Roadmap • Critique of C strings • The need for proper type • String objects
In-Class Exercise Write a C function that copies one string to another. void strcpy(char *dest, char *src) { … }
Assumptions of C Strings • Allocated memory char *str; str = malloc(sizeof(char)*11); • Null termination str[10] = “\0” strcpy(str, “0123456789”);
What if the string is not null terminated? What if the string is not allocated? What can go wrong? void strcpy(char *copy_cat, char *cat) { int j; for (j = 0; cat[j] != ‘\0’; j++) { copy_cat[j] = cat[j]; } copy_cat[j] = ‘\0’; }
Other String Functions • Still assume null-terminated strings • Still assume allocated memory • Security holes… • Internet worms • System break-ins
The Concept of Proper Type • A proper type should be • Responsible for its own data • Data protected from clients and other programs • Responsible for its own behavior • Make behavior available to clients • Implementation hidden from the public view • Responsible for its own existence • Automatic memory management
C++ Class Objects • Responsible for their own data • Protected or private data • Controlled access through public methods • Responsible for their own behavior • Public methods • Responsible for their own existence • Constructors/deconstructors
In-Class Exercise: String Class namespace rcl { class String { // friend… public: // … private: // … } // … }
String Class class String { // friend iostream operators public: // constructors/destructors // operators // builders // data accessors (const) // string comparison function private: // data // helper methods } // equality and order comparison operators
Private Data char *str; unsigned int size; // equal to C strlen(); class String { // friend iostream operators public: // constructors/destructors // operators // builders // data accessors (const) // String comparison function private: // data // helper methods } // equality and order comparison operators
Data Accessors unsigned int Size() const; unsigned int Length() const; char Element(unsigned int n) const; class String { // friend iostream operators public: // constructors/destructors // operators // builders // data accessors (const) // String comparison function private: // data // helper methods } // equality and order comparison operators
Data Accessors unsigned int String::Size() const { return size; } unsigned int String::Length() const { // note: strlen takes char*; therefore, we need to implement an operator that converts String to char*. return strlen(*this); }
In-Class Exercise: Element Return the nth character of the String. Watch out for boundary cases. char String::Element(unsigned int n) const { … }
Element char String::Element(unsigned int n) const { char ch; if ((size == 0) || (n >= size)) { return ‘\0’; } else { return str[n]; } }
String Comparison Function static int StrCmp(const String&, const String&) class String { // friend iostream operators public: // constructors/destructors // operators // builders // data accessors (const) // String comparison function private: // data // helper methods } // equality and order comparison operators
StrCmp int String::StrCmp(const String& S1, const String& S2) { if ((S1.Size() == 0) && (S2.Size() == 0)) { return 0; } else if ((S1.Size() == 0) && (S2.Size() != 0)) { return –1; } else if ((S1.Size() != 0) && (S2.Size() == 0)) { return 1; } else { return (strcmp(S1.str, S2.str); } }
Equality Operators int operator==(const String& S1, const String& S2); int operator!=(const String& S1, const String& S2); int operator<(const String& S1, const String& S2); int operator<=(const String& S1, const String& S2); int operator>=(const String& S1, const String& S2); int operator>(const String& S1, const String& S2); class String { // friend iostream operators public: // constructors/destructors // operators // builders // data accessors (const) // String comparison function private: // data // helper methods } // equality and order comparison operators
Comparison Operators int operator==(const String& S1, const String& S2) { return (String::StrCmp(S1, S2) == 0); } int operator!=(const String& S1, const String& S2) { return (String::StrCmp(S1, S2) != 0); } int operator>(const String& S1, const String& S2) { return (String::StrCmp(S1, S2) > 0); }
Comparison Operators int operator>=(const String& S1, const String& S2) { return (String::StrCmp(S1, S2) >= 0); } int operator<(const String& S1, const String& S2) { return (String::StrCmp(S1, S2) < 0); } int operator<=(const String& S1, const String& S2) { return (String::StrCmp(S1, S2) <= 0); }
Helper Methods static void Error(const char*); static char* newstr(int n); static int xstrlen(const char*); static void xstrcpy(char *, const char*); void Clear(); void Clone(const String& S); class String { // friend iostream operators public: // constructors/destructors // operators // builders // data accessors (const) // String comparison function private: // data // helper methods } // equality and order comparison operators
Error void String::Error(const char* msg) { cerr << “** String error: “ << msg << ‘\n’; exit(EXIT_FAILURE); }
In-Class Exercise: newstr It creates a new String of size n (C String with an array size of n + 1). void String::newstr(int n) { … }
newstr char *String::newstr(int n) { char *Cptr = 0; if (n >= 0) { Cptr = new char[n + 1]; if (Cptr == 0) { Error(“memory allocation failure”) } Cptr[n] = ‘\0’; } return Cptr; }
xstrlen int String::xstrlen(const char* s) { int len; if (s != 0) { // check for null pointer for (len = 0; s[len] != ‘\0’; ++len) { } } return len; }
xstrcpy void String::xstrcpy(char *dest, const char *src) if (src != 0) { // check for null pointer if (dest != 0) { // check for null pointer int j; for (j = 0; src[j] != ‘\0’; ++j) { dest[j] = src[j]; } dest[j] = ‘\0’; } else { Error(“xstrcpy: null destination”); } } }
In-Class Exercise: Clear If the String has storage allocated, delete it, and set the storage pointer and size to zero. void Clear() { … }
Clear void String::Clear() { if (str) { delete[] str; str = 0; size = 0; } }
In-Class Exercise: Clone If makes a copy of the parameter S. void *String::Clone(const String& S) { … }
Clone void String::Clone(const String& S) { size = S.Size(); if (size > 0) { str = newstr(size); xstrcpy(str, S.str); } else { str = 0; } }
Operators String &operator=(const String& S); char &operator[] (unsigned int I) const; operator const char* () const class String { // friend iostream operators public: // constructors/destructors // operators // builders // data accessors (const) // String comparison function private: // data // helper methods } // equality and order comparison operators
Operator= String &String::operator=(const String& S) { if (this != &S) { Clear(); Clone(S); } return *this; }
Operator[] char& String::operator[] (unsigned int n) const { if ((size == 0) || (n > size)) { Error(“index out of range”); } return str[n]; }
Operator const char* String::operator const char* () const { return str; }
Builder Functions void Wrap(const char* Cptr); void GetLine(istream& inl); int SetSize(unsigned int sz, char fill); class String { // friend iostream operators public: // constructors/destructors // operators // builders // data accessors (const) // String comparison function private: // data // helper methods } // equality and order comparison operators
Wrap void String::Wrap(const char* Cptr) { Clear(); if (Cptr) { size = xstrlen(Cptr); str = newstr(size); xstrcpy(str, Cptr); } }
GetLine void String::GetLine(istream& is) { unsigned int curr_size = 0, buff_size = init_buff_size; char *buffer = new char[buff_size + 1]; for (char x = is.get(); ((x != ‘\n’) && (!is.eof()); x = is.get()) { if (curr_size == buff_size) { buff_size *= 2; char *newbuffer = new char[buff_size + 1]; // copy buffer to newbuffer delete[] buffer; buffer = newbuffer; } buffer[curr_size++] = x; } buffer[curr_size] = ‘\0’; Wrap(buffer); delete[] buffer; }
SetSize int String::SetSize(unsigned int sz, char fill) { if (sz != Size()) { char *newdata = newstr(sz); if (newdata == 0) return 0; unsigned int j; if (sz < Size()) { for (j = 0; j < sz; ++j) { newdata[j] = str[j]; } } else { for (j = 0; j < Size(); ++j) { newdata[j] = str[j]; } for (j = Size(); j < sz; ++j) { newdata[j] = fill; } delete[] str; str = newdata; size = sz; } } return 1; }
Constructors/Destructors String(); explicit String(const char* Cptr); ~String(); String (const String &S); class String { // friend iostream operators public: // constructors/destructors // operators // builders // data accessors (const) // String comparison function private: // data // helper methods } // equality and order comparison operators
Constructors /Deconstructors String::String() : str(0), size(0) {} String::String(const char *Cptr) : str(0), size(0) { Wrap(Cptr); } String::String(const String& S) { Clone(S); } String::~String() { if (str) delete[] str; }
I/O Operators friend ostream& operator<<(ostream& os, const String& S); friend istream& operator>>(istream& is, String S); class String { // friend iostream operators public: // constructors/destructors // operators // builders // data accessors (const) // String comparison function private: // data // helper methods } // equality and order comparison operators
Operator<< ostream& operator<<(ostream& os, const String& S) { os << S.str; return os; }
Operator>> istream& operator>>(istream &is, String &S) { unsigned int curr_size = 0; buff_size = init_buff_size; char x; // skip clear space char *buffer = String::newstr(buff_size); buffer[curr_size++] = x; for (x = is.peek(); (// x is not white space or eof); x = is.peek()) { if (curr_size == buff_size) { buff_size *= 2; char *newbuffer = String::newstr(buff_size); // copy buffer into newbuffer delete[] buffer; buffer = newbuffer; } buffer[curr_size++] = x; is.get(); } buffer[curr_size] = ‘\0’; S.Wrap(buffer); delete[] buffer; return is; }