I'm looking to get some feedback on my attempted string implementation. The code is just meant for me to play around with and work on some C++ to try and improve. A lot of the things done here (rule of 4 and 1/2, auto return values, const methods, etc) are features of C++ I don't have a lot or any experience with and I want to get used to them and understand when to use them.
class string
{
public:
explicit string(const char *array = nullptr) :
cString(array ? new char[size]: nullptr), size(array ? strlen(array) + 1: 0), capacity(size)
{
std::copy(array, array + size, cString);
}
string (string& other) :
cString(other.cString ? new char[size] : nullptr), size(other.cString ? strlen(other.cString) + 1 : 0), capacity(size)
{
std::copy(other.cString, other.cString + other.size, cString);
}
string(string&& other) noexcept : string()
{
swap(*this, other);
}
string& operator=(string& other) noexcept
{
swap(*this, other);
return *this;
}
friend std::ostream& operator<<(std::ostream& output, const string& str) noexcept
{
output << str.cString;
return output;
}
inline bool operator==(const string& str) const noexcept
{
return strcmp(cString, str.cString) == 0;
}
inline char operator[](const std::size_t index) const
{
if (index > size) {
throw std::invalid_argument("Index out of bounds");
}
return *(cString + index);
}
inline bool operator!=(const string& str) const noexcept
{
return !(*this == str);
}
string& operator +=(const char *str)
{
if (!str) {
throw std::invalid_argument("Null pointer");
}
std::size_t stringLen = strlen(str);
if (size + stringLen > capacity) {
std::size_t newSize = (size + stringLen + capacity) * 2;
char *newBuffer = new char[newSize];
std::copy(cString, cString + size - 1, newBuffer);
std::copy(str, str + stringLen + 1, newBuffer + size - 1);
delete[] cString;
cString = newBuffer;
capacity = newSize;
} else {
std::copy(cString, cString + size - 1, cString);
std::copy(str, str + stringLen + 1, cString + size - 1);
}
size = size + stringLen;
return *this;
}
friend void swap(string& first, string& second) noexcept
{
using std::swap;
swap(first.size, second.size);
swap(first.cString, second.cString);
}
void clear() noexcept
{
memset(cString, 0, size);
size = 0;
}
static void copy(const string& src, string& dst)
{
if (src.capacity > dst.capacity)
{
char *newBuffer = new char[src.size];
std::copy(src.cString, src.cString + src.size, newBuffer);
delete[] dst.cString;
dst.cString = newBuffer;
dst.size = src.size;
} else {
std::copy(src.cString, src.cString + src.size, dst.cString);
}
}
void replace(const char oldChar, const char newChar) const
{
for(std::size_t i = 0; i < size - 1; i++) {
if (*(cString + i) == oldChar) {
*(cString + i) = newChar;
}
}
}
decltype(auto) hash() const noexcept
{
unsigned long value = 0;
int character;
for (std::size_t i = 0; i < size - 1; i++) {
character = *(cString + i);
value = character + (value << 6) + (value << 16) - value;
}
return value;
}
inline bool isEmpty() const noexcept
{
return size == 0;
}
inline char front() const noexcept
{
return *cString;
}
inline char back() const noexcept
{
return *(cString + size - 2);
}
inline char *getArray() const noexcept
{
return cString;
}
inline decltype(auto) getSize() const noexcept
{
return size;
}
inline decltype(auto) getCapacity() const noexcept
{
return capacity;
}
~string()
{
delete [] cString;
}
private:
std::size_t size;
std::size_t capacity;
char *cString;
};
std::stringto some extent, or is it something completely different? \$\endgroup\$