added implementations for every header
This commit is contained in:
parent
9311aa6938
commit
36dc44ac2d
119
String.cpp
Normal file
119
String.cpp
Normal file
@ -0,0 +1,119 @@
|
||||
#include "String.h"
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
|
||||
|
||||
String::String()
|
||||
:_str(nullptr)
|
||||
{}
|
||||
|
||||
// string must be null-terminated
|
||||
String::String(const char* string)
|
||||
{
|
||||
auto size = std::strlen(string) + 1; // make space for null-terminator
|
||||
auto data = new char[size];
|
||||
std::strcpy(data, string);
|
||||
|
||||
_str = new StringValue(data); // initialize new StringValue with data
|
||||
}
|
||||
|
||||
String::String(const String& other)
|
||||
{
|
||||
_str = other._str;
|
||||
_str->operator++();
|
||||
}
|
||||
|
||||
// copy&move-assignment operator with copy-and-swap
|
||||
String& String::operator=(String other) // take parameter by value: can bind to copy and move-assignment
|
||||
{ // & the compiler can optimize copying
|
||||
swap(*this, other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
String::~String()
|
||||
{
|
||||
_str->operator--(); // the ref-counting is done by StringValue, no need to do that here
|
||||
}
|
||||
|
||||
void swap(String& s1, String& s2) noexcept
|
||||
{
|
||||
using std::swap; // enable ADL, not necessary in our case, yet good practice
|
||||
|
||||
swap(s1._str, s2._str);
|
||||
}
|
||||
|
||||
String::String(String&& other) : String() // init via default ctor so _str is a nullptr
|
||||
{ swap(*this, other); }
|
||||
|
||||
String String::operator+(const String& rhs) const
|
||||
{
|
||||
auto size = this->size() + rhs.size() - 1; // minus one null-terminator, we need only one
|
||||
auto data = new char[size]; // make some space
|
||||
std::strcpy(data, this->c_str()); // copy data of this
|
||||
String ret;
|
||||
ret._str = new StringValue(std::strcat(data, rhs.c_str())); // append data of rsh
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
String& String::operator+=(const String& other)
|
||||
{
|
||||
auto size = this->size() + other.size() - 1; // minus one null-terminator, we need only one
|
||||
auto data = new char[size];
|
||||
std::strcpy(data, this->c_str()); // copy our data
|
||||
std::strcat(data, other.c_str()); // append data of the other string
|
||||
|
||||
_str->operator--(); // release old data
|
||||
_str = new StringValue(data);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// TODO: continue implementing missing stuff from the middle
|
||||
|
||||
bool String::operator==(const String& other) const
|
||||
{ return static_cast<bool>(std::strcmp(this->c_str(), other.c_str())); } // explicit cast to silence warning
|
||||
|
||||
const char& String::operator[](size_t index) const
|
||||
{ return (const_cast<const StringValue*>(_str))->operator[](index); } // cast to use const operator[]
|
||||
|
||||
// reuse const operator[]
|
||||
char& String::operator[](size_t index)
|
||||
{ return const_cast<char&>(const_cast<const StringValue*>(_str)->operator[](index)); }
|
||||
|
||||
size_t String::size() const
|
||||
{ return _str->size(); }
|
||||
|
||||
const char* String::c_str() const
|
||||
{ return *_str; } // invokes StringValue::operator const char*()
|
||||
|
||||
std::istream& operator>>(std::istream& is, String& str)
|
||||
{
|
||||
constexpr size_t BUFSIZE = 50; // set some reasonable buffer size
|
||||
char buf[BUFSIZE];
|
||||
std::unique_ptr<char[]> data(new char[1]); // istream data - I use unique_ptr for exception safety
|
||||
data.get()[0] = '\0'; // init it with a null terminator
|
||||
while (is.get(buf, BUFSIZE)) // while there is still some data in the stream
|
||||
{
|
||||
std::unique_ptr<char[]> newdata(new char[strlen(data.get()) + BUFSIZE]); // make some space
|
||||
std::strcpy(newdata.get(), data.get()); // copy the data already read
|
||||
data.swap(newdata); // make the old data go out of scope instead of newdata
|
||||
std::strcat(data.get(), buf); // append new input
|
||||
}
|
||||
std::unique_ptr<char[]> newdata(new char[strlen(data.get()) + 1]); // shrink the array
|
||||
std::strcpy(newdata.get(), data.get()); // to fit the data
|
||||
data.swap(newdata);
|
||||
|
||||
if (str._str) // if there is some old data, release it
|
||||
str._str->operator--();
|
||||
|
||||
str._str = new StringValue(data.release()); // release ownership of final data
|
||||
|
||||
return is;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const String& str)
|
||||
{
|
||||
return os << str.c_str();
|
||||
}
|
40
StringValue.cpp
Normal file
40
StringValue.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
#include "StringValue.h"
|
||||
#include <stdexcept>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
|
||||
StringValue::StringValue(char* data)
|
||||
:_data(data), _size(std::strlen(data) + 1), _refs(1)
|
||||
{}
|
||||
|
||||
void StringValue::operator++()
|
||||
{ _refs++; }
|
||||
|
||||
void StringValue::operator--()
|
||||
{
|
||||
_refs--;
|
||||
if (_refs == 0)
|
||||
{
|
||||
delete[] _data;
|
||||
delete this;
|
||||
/* This might seem scary, but StringValues are dynamically allocated and own themselves,
|
||||
* thus when no longer referenced the object can -- and should -- commit suicide. */
|
||||
}
|
||||
}
|
||||
|
||||
const char& StringValue::operator[](size_t index) const
|
||||
{
|
||||
if (index > _size-1)
|
||||
throw std::out_of_range("Index out of range!");
|
||||
return _data[index];
|
||||
}
|
||||
|
||||
char& StringValue::operator[](size_t index)
|
||||
{ return const_cast<char&>(const_cast<const StringValue*>(this)->operator[](index)); }
|
||||
|
||||
StringValue::operator const char*() const
|
||||
{ return _data; }
|
||||
|
||||
size_t StringValue::size() const
|
||||
{ return _size - 1; }
|
Loading…
Reference in New Issue
Block a user