// // rwh_string.cpp: sample (naive) string implementation // // note: not including in this group // also: do not use namespace std in this file for clarity #include #include #include template t min(const t& a, const t& b) { return b < a ? b : a; } namespace rwh { // so as to not confuse with std::string // similar to std::string, but uses integer sizes (rather than unsigned) // and has just some of the operations. also has an upper limit of ~1m // and checks that indices are valid class string { int _size; char *_buf; public: static constexpr int maxsize = 1000000; // approximately a megabyte // standard initialization from a c-style string string(const char* src = nullptr) { setto(src, strlen(src)); } // copy constructor string(const string& src) { setto(src.c_buf(), src.size()); } // virtual destructor virtual ~string() { delete [] _buf; } // move constructor string(string&& src) { _buf = src._buf; _size = src._size; src._buf = nullptr; src._size = 0; } // assignment operator string& operator=(const string& src) { if ( this != &src ) { delete [] _buf; setto(src.c_buf(), src.size()); } return *this; } // move assignment operator string& operator=(string&& src) { if ( this != &src ) { delete [] _buf; _buf = src._buf; _size = src._size; src._buf = nullptr; src._size = 0; } return *this; } int size() const { return _size; } // append to end string& operator+=(const string& other) { int max_to_copy = min(other.size(), maxsize - _size); int newsize = _size + max_to_copy; char *newbuf = new char[newsize + 1]; strncpy(newbuf, _buf, _size); strncpy(&newbuf[_size], other._buf, max_to_copy); newbuf[newsize] = '\0'; // std::cout << "append " << _buf << " to " << other._buf << " gives " // << newbuf << std::endl; // std::cout << "_size: " << _size << ", other size: " << other._size // << ", new size: " << newsize << std::endl; delete [] _buf; _buf = newbuf; _size = newsize; return *this; } // index the string in a const context const char& operator[](int index) const { if ( index < 0 ) throw std::out_of_range("negative index to array"); else if ( index > _size ) throw std::out_of_range("buffer overflow error"); return _buf[index]; } // index the string in non-const contexts char& operator[](int index) { if ( index < 0 ) throw std::out_of_range("negative index to array"); else if ( index > _size ) throw std::out_of_range("buffer overflow error"); return _buf[index]; } // returns a c-style string in a read-only, null terminated buffer const char* c_buf() const { return _buf; } // return <0 if *this < other, 0 if the same, >0 if *this > other int compare(const string &other) const { const char *my_p = _buf; const char *other_p = other._buf; while ( *my_p && *other_p && *my_p == *other_p ) { ++my_p; ++other_p; } // if _buf is "ab" and other._buf is "ac", we want a negative // result ('c' - 'b' would do) to say _buf is before other // if _buf is "a" and other._buf is "ac", we again want a negative // result // all other cases are either similar or the opposite return *my_p - *other_p; } bool operator<(const string &other) const { return compare(other) < 0; } bool operator<=(const string &other) const { return compare(other) <= 0; } bool operator==(const string &other) const { return compare(other) == 0; } bool operator>=(const string &other) const { return compare(other) >= 0; } bool operator>(const string &other) const { return compare(other) > 0; } private: // set current string to src's text, allocating _buf first // assumes _buf is not pointing at anything // maximum length of result is maxsize void setto(const char *src, const int new_size) { _size = min(new_size, maxsize); _buf = new char[_size + 1]; strncpy(_buf, src, _size); _buf[_size] = '\0'; } }; } rwh::string operator+(rwh::string a, const rwh::string &b) { a += b; return a; } // supporting printing rwh::string objects, illustrating overload output // note: including iostream also includes std::string; don't use std #include std::ostream& operator<<(std::ostream& out, const rwh::string& str) { out << str.c_buf(); return out; } int main() { rwh::string a("my first"); a[3] = toupper(a[3]); a += " string"; std::cout << a << std::endl; assert(a == "my First string"); assert("my First string" == a); // uses the move constructor: a = rwh::string("this") + rwh::string("that"); assert(a == "thisthat"); // uses the constructor that converts a c-style string to rwh::string a = "hi"; a += " there"; assert(a == "hi there"); // simple checks of c_buf, indexing: assert(a.c_buf()[0] == a[0]); rwh::string one("one"), only("only"); // a first pass at checking comparisons: assert(one < only); assert(one <= only); assert(one <= one); assert(one == one); assert(one < "onemore"); assert(only > one); assert(only >= one); assert(only >= only); assert(rwh::string("") < rwh::string("a")); assert(rwh::string("x") > rwh::string("")); std::cout << "All tests pass.\n"; return 0; }