From 7283633b38a59a6080050475c596a89b57230377 Mon Sep 17 00:00:00 2001 From: Jyatin kumar Singh Date: Fri, 18 Sep 2026 03:51:59 +0530 Subject: [PATCH 01/18] refactor: add production-ready MyString interface --- include/MyString.h | 80 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 include/MyString.h diff --git a/include/MyString.h b/include/MyString.h new file mode 100644 index 0000000..5d71d31 --- /dev/null +++ b/include/MyString.h @@ -0,0 +1,80 @@ +#ifndef MYSTRING_H +#define MYSTRING_H + +#include +#include +#include + +class MyString { +public: + static constexpr std::size_t npos = static_cast(-1); + + MyString(); + explicit MyString(const char* text); + explicit MyString(std::string_view text); + MyString(const MyString& other); + MyString(MyString&& other) noexcept; + ~MyString(); + + MyString& operator=(const MyString& other); + MyString& operator=(MyString&& other) noexcept; + + std::size_t size() const noexcept; + std::size_t length() const noexcept; + std::size_t capacity() const noexcept; + bool empty() const noexcept; + const char* c_str() const noexcept; + char* data() noexcept; + const char* data() const noexcept; + + char& operator[](std::size_t index) noexcept; + const char& operator[](std::size_t index) const noexcept; + char& at(std::size_t index); + const char& at(std::size_t index) const; + char& front(); + const char& front() const; + char& back(); + const char& back() const; + + void clear() noexcept; + void reserve(std::size_t new_capacity); + void swap(MyString& other) noexcept; + + MyString& append(const MyString& other); + MyString& append(const char* text); + MyString& append(char ch); + MyString& operator+=(const MyString& other); + MyString& operator+=(const char* text); + MyString& operator+=(char ch); + + std::size_t find(char ch, std::size_t pos = 0) const noexcept; + std::size_t find(const MyString& needle, std::size_t pos = 0) const noexcept; + bool contains(const MyString& needle) const noexcept; + bool starts_with(const MyString& prefix) const noexcept; + bool ends_with(const MyString& suffix) const noexcept; + MyString substr(std::size_t pos, std::size_t count = npos) const; + MyString reversed() const; + + int compare(const MyString& other) const noexcept; + + friend MyString operator+(const MyString& lhs, const MyString& rhs); + friend bool operator==(const MyString& lhs, const MyString& rhs) noexcept; + friend bool operator!=(const MyString& lhs, const MyString& rhs) noexcept; + friend bool operator<(const MyString& lhs, const MyString& rhs) noexcept; + friend bool operator<=(const MyString& lhs, const MyString& rhs) noexcept; + friend bool operator>(const MyString& lhs, const MyString& rhs) noexcept; + friend bool operator>=(const MyString& lhs, const MyString& rhs) noexcept; + friend std::ostream& operator<<(std::ostream& out, const MyString& value); + friend std::istream& operator>>(std::istream& in, MyString& value); + +private: + char* data_; + std::size_t size_; + std::size_t capacity_; + + static std::size_t text_length(const char* text) noexcept; + static void copy_n(char* destination, const char* source, std::size_t count) noexcept; + void ensure_capacity(std::size_t required); +}; + +#endif From e2efd0d24ea3bdf0efcfac5321e8937add9ebceb Mon Sep 17 00:00:00 2001 From: Jyatin kumar Singh Date: Fri, 18 Sep 2026 03:52:28 +0530 Subject: [PATCH 02/18] refactor: implement Rule of Five and string operations --- src/MyString.cpp | 275 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 275 insertions(+) create mode 100644 src/MyString.cpp diff --git a/src/MyString.cpp b/src/MyString.cpp new file mode 100644 index 0000000..dc0dd4f --- /dev/null +++ b/src/MyString.cpp @@ -0,0 +1,275 @@ +#include "MyString.h" + +#include +#include +#include +#include +#include +#include + +std::size_t MyString::text_length(const char* text) noexcept { + if (text == nullptr) { + return 0; + } + std::size_t length = 0; + while (text[length] != '\0') { + ++length; + } + return length; +} + +void MyString::copy_n(char* destination, const char* source, std::size_t count) noexcept { + for (std::size_t i = 0; i < count; ++i) { + destination[i] = source[i]; + } +} + +MyString::MyString() : data_(new char[1]{'\0'}), size_(0), capacity_(0) {} + +MyString::MyString(const char* text) : MyString(std::string_view(text == nullptr ? "" : text)) {} + +MyString::MyString(std::string_view text) + : data_(new char[text.size() + 1]), size_(text.size()), capacity_(text.size()) { + copy_n(data_, text.data(), size_); + data_[size_] = '\0'; +} + +MyString::MyString(const MyString& other) + : data_(new char[other.size_ + 1]), size_(other.size_), capacity_(other.size_) { + copy_n(data_, other.data_, size_); + data_[size_] = '\0'; +} + +MyString::MyString(MyString&& other) noexcept + : data_(other.data_), size_(other.size_), capacity_(other.capacity_) { + other.data_ = new char[1]{'\0'}; + other.size_ = 0; + other.capacity_ = 0; +} + +MyString::~MyString() { + delete[] data_; +} + +MyString& MyString::operator=(const MyString& other) { + if (this == &other) { + return *this; + } + MyString copy(other); + swap(copy); + return *this; +} + +MyString& MyString::operator=(MyString&& other) noexcept { + if (this == &other) { + return *this; + } + delete[] data_; + data_ = other.data_; + size_ = other.size_; + capacity_ = other.capacity_; + other.data_ = new char[1]{'\0'}; + other.size_ = 0; + other.capacity_ = 0; + return *this; +} + +std::size_t MyString::size() const noexcept { return size_; } +std::size_t MyString::length() const noexcept { return size_; } +std::size_t MyString::capacity() const noexcept { return capacity_; } +bool MyString::empty() const noexcept { return size_ == 0; } +const char* MyString::c_str() const noexcept { return data_; } +char* MyString::data() noexcept { return data_; } +const char* MyString::data() const noexcept { return data_; } + +char& MyString::operator[](std::size_t index) noexcept { return data_[index]; } +const char& MyString::operator[](std::size_t index) const noexcept { return data_[index]; } + +char& MyString::at(std::size_t index) { + if (index >= size_) throw std::out_of_range("MyString::at: index out of range"); + return data_[index]; +} + +const char& MyString::at(std::size_t index) const { + if (index >= size_) throw std::out_of_range("MyString::at: index out of range"); + return data_[index]; +} + +char& MyString::front() { + if (empty()) throw std::out_of_range("MyString::front: empty string"); + return data_[0]; +} + +const char& MyString::front() const { + if (empty()) throw std::out_of_range("MyString::front: empty string"); + return data_[0]; +} + +char& MyString::back() { + if (empty()) throw std::out_of_range("MyString::back: empty string"); + return data_[size_ - 1]; +} + +const char& MyString::back() const { + if (empty()) throw std::out_of_range("MyString::back: empty string"); + return data_[size_ - 1]; +} + +void MyString::clear() noexcept { + size_ = 0; + data_[0] = '\0'; +} + +void MyString::ensure_capacity(std::size_t required) { + if (required <= capacity_) return; + std::size_t new_capacity = std::max(required, capacity_ == 0 ? std::size_t{1} : capacity_ * 2); + char* replacement = new char[new_capacity + 1]; + copy_n(replacement, data_, size_); + replacement[size_] = '\0'; + delete[] data_; + data_ = replacement; + capacity_ = new_capacity; +} + +void MyString::reserve(std::size_t new_capacity) { + if (new_capacity <= capacity_) return; + char* replacement = new char[new_capacity + 1]; + copy_n(replacement, data_, size_); + replacement[size_] = '\0'; + delete[] data_; + data_ = replacement; + capacity_ = new_capacity; +} + +void MyString::swap(MyString& other) noexcept { + using std::swap; + swap(data_, other.data_); + swap(size_, other.size_); + swap(capacity_, other.capacity_); +} + +MyString& MyString::append(const MyString& other) { + if (this == &other) { + MyString copy(other); + return append(copy); + } + ensure_capacity(size_ + other.size_); + copy_n(data_ + size_, other.data_, other.size_); + size_ += other.size_; + data_[size_] = '\0'; + return *this; +} + +MyString& MyString::append(const char* text) { + if (text == nullptr) return *this; + const std::size_t count = text_length(text); + if (count == 0) return *this; + if (text >= data_ && text <= data_ + size_) { + MyString copy(text); + return append(copy); + } + ensure_capacity(size_ + count); + copy_n(data_ + size_, text, count); + size_ += count; + data_[size_] = '\0'; + return *this; +} + +MyString& MyString::append(char ch) { + ensure_capacity(size_ + 1); + data_[size_++] = ch; + data_[size_] = '\0'; + return *this; +} + +MyString& MyString::operator+=(const MyString& other) { return append(other); } +MyString& MyString::operator+=(const char* text) { return append(text); } +MyString& MyString::operator+=(char ch) { return append(ch); } + +std::size_t MyString::find(char ch, std::size_t pos) const noexcept { + for (std::size_t i = pos; i < size_; ++i) { + if (data_[i] == ch) return i; + } + return npos; +} + +std::size_t MyString::find(const MyString& needle, std::size_t pos) const noexcept { + if (needle.empty()) return pos <= size_ ? pos : npos; + if (needle.size_ > size_ || pos > size_ - needle.size_) return npos; + for (std::size_t i = pos; i <= size_ - needle.size_; ++i) { + std::size_t j = 0; + while (j < needle.size_ && data_[i + j] == needle.data_[j]) ++j; + if (j == needle.size_) return i; + } + return npos; +} + +bool MyString::contains(const MyString& needle) const noexcept { return find(needle) != npos; } + +bool MyString::starts_with(const MyString& prefix) const noexcept { + if (prefix.size_ > size_) return false; + for (std::size_t i = 0; i < prefix.size_; ++i) if (data_[i] != prefix.data_[i]) return false; + return true; +} + +bool MyString::ends_with(const MyString& suffix) const noexcept { + if (suffix.size_ > size_) return false; + const std::size_t offset = size_ - suffix.size_; + for (std::size_t i = 0; i < suffix.size_; ++i) if (data_[offset + i] != suffix.data_[i]) return false; + return true; +} + +MyString MyString::substr(std::size_t pos, std::size_t count) const { + if (pos > size_) throw std::out_of_range("MyString::substr: position out of range"); + const std::size_t actual = std::min(count, size_ - pos); + return MyString(std::string_view(data_ + pos, actual)); +} + +MyString MyString::reversed() const { + MyString result(*this); + for (std::size_t left = 0, right = size_ == 0 ? 0 : size_ - 1; left < right; ++left, --right) { + std::swap(result.data_[left], result.data_[right]); + } + return result; +} + +int MyString::compare(const MyString& other) const noexcept { + const std::size_t common = std::min(size_, other.size_); + for (std::size_t i = 0; i < common; ++i) { + if (data_[i] < other.data_[i]) return -1; + if (data_[i] > other.data_[i]) return 1; + } + if (size_ < other.size_) return -1; + if (size_ > other.size_) return 1; + return 0; +} + +MyString operator+(const MyString& lhs, const MyString& rhs) { + MyString result; + result.reserve(lhs.size_ + rhs.size_); + result.append(lhs); + result.append(rhs); + return result; +} + +bool operator==(const MyString& lhs, const MyString& rhs) noexcept { return lhs.compare(rhs) == 0; } +bool operator!=(const MyString& lhs, const MyString& rhs) noexcept { return !(lhs == rhs); } +bool operator<(const MyString& lhs, const MyString& rhs) noexcept { return lhs.compare(rhs) < 0; } +bool operator<=(const MyString& lhs, const MyString& rhs) noexcept { return lhs.compare(rhs) <= 0; } +bool operator>(const MyString& lhs, const MyString& rhs) noexcept { return lhs.compare(rhs) > 0; } +bool operator>=(const MyString& lhs, const MyString& rhs) noexcept { return lhs.compare(rhs) >= 0; } + +std::ostream& operator<<(std::ostream& out, const MyString& value) { + return out.write(value.data_, static_cast(value.size_)); +} + +std::istream& operator>>(std::istream& in, MyString& value) { + value.clear(); + in >> std::ws; + while (in.good()) { + const int next = in.peek(); + if (next == std::char_traits::eof() || std::isspace(static_cast(next))) break; + value.append(static_cast(in.get())); + } + return in; +} From 21181177de051b64078fbb21d120d90c91a1fa62 Mon Sep 17 00:00:00 2001 From: Jyatin kumar Singh Date: Fri, 18 Sep 2026 03:53:12 +0530 Subject: [PATCH 03/18] fix: make moved-from MyString safely reusable --- src/MyString.cpp | 54 +++++++++++++++--------------------------------- 1 file changed, 17 insertions(+), 37 deletions(-) diff --git a/src/MyString.cpp b/src/MyString.cpp index dc0dd4f..b07ca82 100644 --- a/src/MyString.cpp +++ b/src/MyString.cpp @@ -8,24 +8,17 @@ #include std::size_t MyString::text_length(const char* text) noexcept { - if (text == nullptr) { - return 0; - } + if (text == nullptr) return 0; std::size_t length = 0; - while (text[length] != '\0') { - ++length; - } + while (text[length] != '\0') ++length; return length; } void MyString::copy_n(char* destination, const char* source, std::size_t count) noexcept { - for (std::size_t i = 0; i < count; ++i) { - destination[i] = source[i]; - } + for (std::size_t i = 0; i < count; ++i) destination[i] = source[i]; } MyString::MyString() : data_(new char[1]{'\0'}), size_(0), capacity_(0) {} - MyString::MyString(const char* text) : MyString(std::string_view(text == nullptr ? "" : text)) {} MyString::MyString(std::string_view text) @@ -42,33 +35,27 @@ MyString::MyString(const MyString& other) MyString::MyString(MyString&& other) noexcept : data_(other.data_), size_(other.size_), capacity_(other.capacity_) { - other.data_ = new char[1]{'\0'}; + other.data_ = nullptr; other.size_ = 0; other.capacity_ = 0; } -MyString::~MyString() { - delete[] data_; -} +MyString::~MyString() { delete[] data_; } MyString& MyString::operator=(const MyString& other) { - if (this == &other) { - return *this; - } + if (this == &other) return *this; MyString copy(other); swap(copy); return *this; } MyString& MyString::operator=(MyString&& other) noexcept { - if (this == &other) { - return *this; - } + if (this == &other) return *this; delete[] data_; data_ = other.data_; size_ = other.size_; capacity_ = other.capacity_; - other.data_ = new char[1]{'\0'}; + other.data_ = nullptr; other.size_ = 0; other.capacity_ = 0; return *this; @@ -78,9 +65,9 @@ std::size_t MyString::size() const noexcept { return size_; } std::size_t MyString::length() const noexcept { return size_; } std::size_t MyString::capacity() const noexcept { return capacity_; } bool MyString::empty() const noexcept { return size_ == 0; } -const char* MyString::c_str() const noexcept { return data_; } +const char* MyString::c_str() const noexcept { return data_ != nullptr ? data_ : ""; } char* MyString::data() noexcept { return data_; } -const char* MyString::data() const noexcept { return data_; } +const char* MyString::data() const noexcept { return data_ != nullptr ? data_ : ""; } char& MyString::operator[](std::size_t index) noexcept { return data_[index]; } const char& MyString::operator[](std::size_t index) const noexcept { return data_[index]; } @@ -89,40 +76,36 @@ char& MyString::at(std::size_t index) { if (index >= size_) throw std::out_of_range("MyString::at: index out of range"); return data_[index]; } - const char& MyString::at(std::size_t index) const { if (index >= size_) throw std::out_of_range("MyString::at: index out of range"); return data_[index]; } - char& MyString::front() { if (empty()) throw std::out_of_range("MyString::front: empty string"); return data_[0]; } - const char& MyString::front() const { if (empty()) throw std::out_of_range("MyString::front: empty string"); return data_[0]; } - char& MyString::back() { if (empty()) throw std::out_of_range("MyString::back: empty string"); return data_[size_ - 1]; } - const char& MyString::back() const { if (empty()) throw std::out_of_range("MyString::back: empty string"); return data_[size_ - 1]; } void MyString::clear() noexcept { + if (data_ == nullptr) data_ = new char[1]{'\0'}; size_ = 0; data_[0] = '\0'; } void MyString::ensure_capacity(std::size_t required) { if (required <= capacity_) return; - std::size_t new_capacity = std::max(required, capacity_ == 0 ? std::size_t{1} : capacity_ * 2); + const std::size_t new_capacity = std::max(required, capacity_ == 0 ? std::size_t{1} : capacity_ * 2); char* replacement = new char[new_capacity + 1]; copy_n(replacement, data_, size_); replacement[size_] = '\0'; @@ -164,7 +147,7 @@ MyString& MyString::append(const char* text) { if (text == nullptr) return *this; const std::size_t count = text_length(text); if (count == 0) return *this; - if (text >= data_ && text <= data_ + size_) { + if (data_ != nullptr && text >= data_ && text <= data_ + size_) { MyString copy(text); return append(copy); } @@ -187,9 +170,7 @@ MyString& MyString::operator+=(const char* text) { return append(text); } MyString& MyString::operator+=(char ch) { return append(ch); } std::size_t MyString::find(char ch, std::size_t pos) const noexcept { - for (std::size_t i = pos; i < size_; ++i) { - if (data_[i] == ch) return i; - } + for (std::size_t i = pos; i < size_; ++i) if (data_[i] == ch) return i; return npos; } @@ -222,14 +203,13 @@ bool MyString::ends_with(const MyString& suffix) const noexcept { MyString MyString::substr(std::size_t pos, std::size_t count) const { if (pos > size_) throw std::out_of_range("MyString::substr: position out of range"); const std::size_t actual = std::min(count, size_ - pos); - return MyString(std::string_view(data_ + pos, actual)); + return MyString(std::string_view(data(), actual)); } MyString MyString::reversed() const { MyString result(*this); - for (std::size_t left = 0, right = size_ == 0 ? 0 : size_ - 1; left < right; ++left, --right) { + for (std::size_t left = 0, right = size_ == 0 ? 0 : size_ - 1; left < right; ++left, --right) std::swap(result.data_[left], result.data_[right]); - } return result; } @@ -260,7 +240,7 @@ bool operator>(const MyString& lhs, const MyString& rhs) noexcept { return lhs.c bool operator>=(const MyString& lhs, const MyString& rhs) noexcept { return lhs.compare(rhs) >= 0; } std::ostream& operator<<(std::ostream& out, const MyString& value) { - return out.write(value.data_, static_cast(value.size_)); + return out.write(value.c_str(), static_cast(value.size_)); } std::istream& operator>>(std::istream& in, MyString& value) { From a4d7c881428c18386b6d0a0994adf9c3e3ce30ea Mon Sep 17 00:00:00 2001 From: Jyatin kumar Singh Date: Fri, 18 Sep 2026 03:53:25 +0530 Subject: [PATCH 04/18] fix: use noexcept-safe moved-from sentinel --- include/MyString.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/MyString.h b/include/MyString.h index 5d71d31..e148f69 100644 --- a/include/MyString.h +++ b/include/MyString.h @@ -71,6 +71,7 @@ class MyString { char* data_; std::size_t size_; std::size_t capacity_; + static char empty_data_; static std::size_t text_length(const char* text) noexcept; static void copy_n(char* destination, const char* source, std::size_t count) noexcept; From 058c3c1f2e7dc41585ed01f0ff07f6d3222ef090 Mon Sep 17 00:00:00 2001 From: Jyatin kumar Singh Date: Fri, 18 Sep 2026 03:53:46 +0530 Subject: [PATCH 05/18] fix: make moved-from state allocation-free and safe --- src/MyString.cpp | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/MyString.cpp b/src/MyString.cpp index b07ca82..ebbe3aa 100644 --- a/src/MyString.cpp +++ b/src/MyString.cpp @@ -7,6 +7,8 @@ #include #include +char MyString::empty_data_ = '\0'; + std::size_t MyString::text_length(const char* text) noexcept { if (text == nullptr) return 0; std::size_t length = 0; @@ -35,12 +37,14 @@ MyString::MyString(const MyString& other) MyString::MyString(MyString&& other) noexcept : data_(other.data_), size_(other.size_), capacity_(other.capacity_) { - other.data_ = nullptr; + other.data_ = &empty_data_; other.size_ = 0; other.capacity_ = 0; } -MyString::~MyString() { delete[] data_; } +MyString::~MyString() { + if (data_ != &empty_data_) delete[] data_; +} MyString& MyString::operator=(const MyString& other) { if (this == &other) return *this; @@ -51,11 +55,11 @@ MyString& MyString::operator=(const MyString& other) { MyString& MyString::operator=(MyString&& other) noexcept { if (this == &other) return *this; - delete[] data_; + if (data_ != &empty_data_) delete[] data_; data_ = other.data_; size_ = other.size_; capacity_ = other.capacity_; - other.data_ = nullptr; + other.data_ = &empty_data_; other.size_ = 0; other.capacity_ = 0; return *this; @@ -65,9 +69,9 @@ std::size_t MyString::size() const noexcept { return size_; } std::size_t MyString::length() const noexcept { return size_; } std::size_t MyString::capacity() const noexcept { return capacity_; } bool MyString::empty() const noexcept { return size_ == 0; } -const char* MyString::c_str() const noexcept { return data_ != nullptr ? data_ : ""; } +const char* MyString::c_str() const noexcept { return data_; } char* MyString::data() noexcept { return data_; } -const char* MyString::data() const noexcept { return data_ != nullptr ? data_ : ""; } +const char* MyString::data() const noexcept { return data_; } char& MyString::operator[](std::size_t index) noexcept { return data_[index]; } const char& MyString::operator[](std::size_t index) const noexcept { return data_[index]; } @@ -98,7 +102,7 @@ const char& MyString::back() const { } void MyString::clear() noexcept { - if (data_ == nullptr) data_ = new char[1]{'\0'}; + if (data_ == &empty_data_) return; size_ = 0; data_[0] = '\0'; } @@ -109,7 +113,7 @@ void MyString::ensure_capacity(std::size_t required) { char* replacement = new char[new_capacity + 1]; copy_n(replacement, data_, size_); replacement[size_] = '\0'; - delete[] data_; + if (data_ != &empty_data_) delete[] data_; data_ = replacement; capacity_ = new_capacity; } @@ -119,7 +123,7 @@ void MyString::reserve(std::size_t new_capacity) { char* replacement = new char[new_capacity + 1]; copy_n(replacement, data_, size_); replacement[size_] = '\0'; - delete[] data_; + if (data_ != &empty_data_) delete[] data_; data_ = replacement; capacity_ = new_capacity; } @@ -147,7 +151,7 @@ MyString& MyString::append(const char* text) { if (text == nullptr) return *this; const std::size_t count = text_length(text); if (count == 0) return *this; - if (data_ != nullptr && text >= data_ && text <= data_ + size_) { + if (data_ != &empty_data_ && text >= data_ && text <= data_ + size_) { MyString copy(text); return append(copy); } @@ -203,7 +207,7 @@ bool MyString::ends_with(const MyString& suffix) const noexcept { MyString MyString::substr(std::size_t pos, std::size_t count) const { if (pos > size_) throw std::out_of_range("MyString::substr: position out of range"); const std::size_t actual = std::min(count, size_ - pos); - return MyString(std::string_view(data(), actual)); + return MyString(std::string_view(data_, actual)); } MyString MyString::reversed() const { From e212d47d04452f3a764ffb572393843e443cea06 Mon Sep 17 00:00:00 2001 From: Jyatin kumar Singh Date: Fri, 18 Sep 2026 03:53:56 +0530 Subject: [PATCH 06/18] feat: add interactive MyString demonstration --- examples/main.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 examples/main.cpp diff --git a/examples/main.cpp b/examples/main.cpp new file mode 100644 index 0000000..1677145 --- /dev/null +++ b/examples/main.cpp @@ -0,0 +1,43 @@ +#include +#include + +#include "MyString.h" + +int main() { + MyString language("Modern C++17"); + MyString project("MyString"); + + std::cout << "=== MyString demo ===\n"; + std::cout << "language : " << language << '\n'; + std::cout << "project : " << project << '\n'; + + MyString combined = language + " - " + project; + std::cout << "combined : " << combined << '\n'; + std::cout << "size : " << combined.size() << '\n'; + std::cout << "capacity : " << combined.capacity() << '\n'; + + combined += " library"; + std::cout << "append : " << combined << '\n'; + std::cout << "find : " << combined.find(MyString("String")) << '\n'; + std::cout << "contains : " << std::boolalpha << combined.contains(MyString("C++")) << '\n'; + std::cout << "prefix : " << combined.starts_with(MyString("Modern")) << '\n'; + std::cout << "suffix : " << combined.ends_with(MyString("library")) << '\n'; + std::cout << "substr : " << combined.substr(7, 5) << '\n'; + std::cout << "reverse : " << combined.reversed() << '\n'; + + MyString copied(combined); + copied[0] = 'm'; + std::cout << "copy : " << copied << '\n'; + std::cout << "original : " << combined << '\n'; + + MyString moved(std::move(copied)); + std::cout << "moved : " << moved << '\n'; + std::cout << "moved-from size: " << copied.size() << '\n'; + + MyString cleared = combined; + cleared.clear(); + std::cout << "cleared : \"" << cleared << "\" (empty=" << cleared.empty() << ")\n"; + + std::cout << "=== demo complete ===\n"; + return 0; +} From 6fec331da2de9ec7871cfcc751417c09edeae154 Mon Sep 17 00:00:00 2001 From: Jyatin kumar Singh Date: Fri, 18 Sep 2026 03:54:04 +0530 Subject: [PATCH 07/18] fix: use explicit MyString concatenation in demo --- examples/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/main.cpp b/examples/main.cpp index 1677145..60f44aa 100644 --- a/examples/main.cpp +++ b/examples/main.cpp @@ -11,7 +11,8 @@ int main() { std::cout << "language : " << language << '\n'; std::cout << "project : " << project << '\n'; - MyString combined = language + " - " + project; + MyString combined = language + MyString(" - "); + combined += project; std::cout << "combined : " << combined << '\n'; std::cout << "size : " << combined.size() << '\n'; std::cout << "capacity : " << combined.capacity() << '\n'; From 0c87bf0c110decb0b28b5594d968fbfdfd58b708 Mon Sep 17 00:00:00 2001 From: Jyatin kumar Singh Date: Fri, 18 Sep 2026 03:54:15 +0530 Subject: [PATCH 08/18] test: add comprehensive MyString test suite --- tests/test_mystring.cpp | 116 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 tests/test_mystring.cpp diff --git a/tests/test_mystring.cpp b/tests/test_mystring.cpp new file mode 100644 index 0000000..2520c83 --- /dev/null +++ b/tests/test_mystring.cpp @@ -0,0 +1,116 @@ +#include +#include +#include +#include + +#include "MyString.h" + +static void test_construction_and_access() { + MyString empty; + assert(empty.empty()); + assert(empty.size() == 0); + assert(empty.c_str()[0] == '\0'); + + MyString value("hello"); + assert(value.size() == 5); + assert(value.length() == 5); + assert(value.front() == 'h'); + assert(value.back() == 'o'); + value[0] = 'H'; + assert(value == MyString("Hello")); + assert(value.at(1) == 'e'); + + bool threw = false; + try { (void)value.at(99); } catch (const std::out_of_range&) { threw = true; } + assert(threw); +} + +static void test_rule_of_five() { + MyString original("resource"); + MyString copied(original); + copied[0] = 'R'; + assert(original == MyString("resource")); + assert(copied == MyString("Resource")); + + MyString assigned; + assigned = original; + assert(assigned == original); + + MyString moved(std::move(copied)); + assert(moved == MyString("Resource")); + assert(copied.empty()); + assert(copied.c_str()[0] == '\0'); + + MyString move_assigned; + move_assigned = std::move(moved); + assert(move_assigned == MyString("Resource")); + assert(moved.empty()); +} + +static void test_modification_and_capacity() { + MyString value("ab"); + value.reserve(32); + assert(value.capacity() >= 32); + value.append('c').append("def"); + assert(value == MyString("abcdef")); + value += MyString("!"); + assert(value == MyString("abcdef!")); + + value += value; + assert(value == MyString("abcdef!abcdef!")); + + const std::size_t old_capacity = value.capacity(); + value.clear(); + assert(value.empty()); + assert(value.capacity() == old_capacity); +} + +static void test_search_and_substrings() { + MyString value("banana bread"); + assert(value.find('b') == 0); + assert(value.find('x') == MyString::npos); + assert(value.find(MyString("bread")) == 7); + assert(value.contains(MyString("ana"))); + assert(!value.contains(MyString("cake"))); + assert(value.starts_with(MyString("ban"))); + assert(value.ends_with(MyString("bread"))); + assert(value.substr(7) == MyString("bread")); + assert(value.substr(0, 6) == MyString("banana")); + assert(value.reversed() == MyString("daerb ananab")); +} + +static void test_comparison_and_streams() { + assert(MyString("abc") < MyString("abd")); + assert(MyString("abc") <= MyString("abc")); + assert(MyString("abd") > MyString("abc")); + assert(MyString("abc") != MyString("abd")); + + std::stringstream input("hello world"); + MyString first; + MyString second; + input >> first >> second; + assert(first == MyString("hello")); + assert(second == MyString("world")); + + std::stringstream output; + output << first << ' ' << second; + assert(output.str() == "hello world"); +} + +static void test_embedded_nulls() { + const char raw[] = {'a', '\0', 'b'}; + MyString value(std::string_view(raw, 3)); + assert(value.size() == 3); + assert(value[1] == '\0'); + assert(value.substr(1, 2).size() == 2); +} + +int main() { + test_construction_and_access(); + test_rule_of_five(); + test_modification_and_capacity(); + test_search_and_substrings(); + test_comparison_and_streams(); + test_embedded_nulls(); + return 0; +} From 44cb784d64e4e4bfd5d1304333aba1d43423c663 Mon Sep 17 00:00:00 2001 From: Jyatin kumar Singh Date: Fri, 18 Sep 2026 03:54:25 +0530 Subject: [PATCH 09/18] fix: include exception type in test suite --- tests/test_mystring.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_mystring.cpp b/tests/test_mystring.cpp index 2520c83..254bf7e 100644 --- a/tests/test_mystring.cpp +++ b/tests/test_mystring.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include From 5341a580dee0df9088e7121cea8bd9fac26a914c Mon Sep 17 00:00:00 2001 From: Jyatin kumar Singh Date: Fri, 18 Sep 2026 03:54:30 +0530 Subject: [PATCH 10/18] build: add portable CMake project --- CMakeLists.txt | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..ffa6d1a --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.16) + +project(MyString VERSION 1.0.0 LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +add_library(mystring src/MyString.cpp) +target_include_directories(mystring PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_compile_options(mystring PRIVATE + $<$:-Wall;-Wextra;-Wpedantic> + $<$:/W4> +) + +add_executable(mystring_demo examples/main.cpp) +target_link_libraries(mystring_demo PRIVATE mystring) + +include(CTest) +if(BUILD_TESTING) + add_executable(mystring_tests tests/test_mystring.cpp) + target_link_libraries(mystring_tests PRIVATE mystring) + add_test(NAME MyStringTests COMMAND mystring_tests) +endif() From 17da46785c7d66cc9912791f311b0daf7acc285a Mon Sep 17 00:00:00 2001 From: Jyatin kumar Singh Date: Fri, 18 Sep 2026 03:54:35 +0530 Subject: [PATCH 11/18] ci: add automated build tests and sanitizers --- .github/workflows/ci.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..20f3746 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,31 @@ +name: C++ CI + +on: + push: + branches: [main, feature/production-grade-mystring] + pull_request: + branches: [main] + +jobs: + build-and-test: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Configure + run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug + + - name: Build + run: cmake --build build --parallel + + - name: Test + run: ctest --test-dir build --output-on-failure + + - name: Sanitizer build + run: | + cmake -S . -B build-asan -DCMAKE_BUILD_TYPE=Debug \ + -DCMAKE_CXX_FLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer" + cmake --build build-asan --parallel + ctest --test-dir build-asan --output-on-failure From fb5624595d5273b706b3bc4e0ba395c66df67913 Mon Sep 17 00:00:00 2001 From: Jyatin kumar Singh Date: Fri, 18 Sep 2026 03:54:58 +0530 Subject: [PATCH 12/18] docs: replace README with complete project documentation --- README.md | 195 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 156 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index d6ccfc5..eb2fde3 100644 --- a/README.md +++ b/README.md @@ -1,64 +1,181 @@ - # Custom MyString Class in C++ +# MyString — a small `std::string`-style class built from scratch -A custom implementation of a `String` class in C++ built from scratch without using `std::string`. This project demonstrates object-oriented programming, dynamic memory management, deep copying, and operator overloading. +[![C++ CI](https://github.com/Jyatin/MyString/actions/workflows/ci.yml/badge.svg)](https://github.com/Jyatin/MyString/actions/workflows/ci.yml) +[![C++17](https://img.shields.io/badge/C%2B%2B-17-00599C.svg)](https://en.cppreference.com/w/cpp/17) -## Features +A production-minded custom string implementation in modern C++17. The goal is not to replace `std::string`, but to make the mechanics behind a dynamic string explicit: ownership, heap allocation, deep copying, move semantics, capacity growth, bounds checking, searching, and operator overloading. -- Dynamic memory allocation -- Constructors and Destructor -- Copy Constructor -- Copy Assignment Operator -- Deep Copy Implementation -- String Length -- Character Access -- String Comparison -- Operator Overloading +> **Constraint:** the implementation does not use `std::string` for its internal storage. It owns a null-terminated `char` buffer and manages that buffer itself. -## Project Structure +## What changed + +The original project was a compact Rule-of-Five demonstration. It is now organised as a small library with a demo, automated tests, CMake build support, and GitHub Actions CI. + +### Core functionality + +- Rule of Five: destructor, copy constructor/assignment, move constructor/assignment +- Dynamic heap-backed storage with geometric capacity growth +- Stronger copy-assignment exception safety using copy-and-swap +- `size()`, `length()`, `capacity()`, `empty()`, `clear()`, `reserve()` +- `operator[]`, checked `at()`, `front()`, `back()` +- `c_str()` and mutable/const `data()` access +- `append()` and `operator+=` for strings and characters +- Concatenation with `operator+` +- Equality and lexicographical relational operators +- `find()` for characters and substrings +- `contains()`, `starts_with()`, `ends_with()` +- `substr()` and non-mutating `reversed()` +- Stream insertion/extraction (`<<`, `>>`) +- `std::string_view` construction, including strings containing embedded `\0` bytes +- Self-append protection and a reusable moved-from state + +## Project structure ```text -Custom-MyString-CPP/ -│ -├── Main.cpp # Driver program -├── MyString.h # Class declaration -└── MyString.cpp # Class implementation +MyString/ +├── include/ +│ └── MyString.h # Public class interface +├── src/ +│ └── MyString.cpp # Memory management + implementation +├── examples/ +│ └── main.cpp # Feature demonstration +├── tests/ +│ └── test_mystring.cpp # Assertion-based test suite +├── .github/ +│ └── workflows/ +│ └── ci.yml # Build, test and sanitizer CI +├── CMakeLists.txt # Portable build configuration +└── README.md ``` -## Technologies Used +## Quick start -- C++ -- Git -- GitHub -- Visual Studio Code +### GCC / Clang -## Build & Run +```bash +g++ -std=c++17 -Wall -Wextra -Wpedantic \ + -Iinclude src/MyString.cpp examples/main.cpp \ + -o mystring_demo + +./mystring_demo +``` -Compile the project using: +### CMake ```bash -g++ Main.cpp MyString.cpp -o MyString +cmake -S . -B build -DCMAKE_BUILD_TYPE=Release +cmake --build build --parallel +ctest --test-dir build --output-on-failure +./build/mystring_demo ``` -Run: +On Windows with a Visual Studio generator, build the same CMake targets from the generated solution or with `cmake --build build --config Release`. -**Windows** -```bash -MyString.exe +## Demo output + +The example exercises construction, concatenation, capacity, append, search, prefix/suffix checks, substring extraction, reversal, deep copy, move construction, and clearing. + +```text +=== MyString demo === +language : Modern C++17 +project : MyString +combined : Modern C++17 - MyString +size : 24 +capacity : 24 +append : Modern C++17 - MyString library +find : 17 +contains : true +prefix : true +suffix : true +substr : C++17 +reverse : yrarbil gnirtSyM - 71++C nredoM +copy : modern C++17 - MyString library +original : Modern C++17 - MyString library +moved : modern C++17 - MyString library +moved-from size: 0 +cleared : "" (empty=true) +=== demo complete === ``` -**Linux/macOS** +The CI workflow builds this demo and runs the test suite on pushes and pull requests. It also runs the tests under AddressSanitizer and UndefinedBehaviorSanitizer on Ubuntu. + +## API at a glance + +| API | Purpose | Complexity | +|---|---|---:| +| `size()` / `length()` | Current number of characters | O(1) | +| `capacity()` | Allocated character capacity | O(1) | +| `operator[]` | Unchecked indexed access | O(1) | +| `at()` | Bounds-checked indexed access | O(1) | +| `append()` / `+=` | Add characters or another string | Amortized O(n) | +| `reserve()` | Pre-allocate capacity | O(n) when reallocation occurs | +| `find()` | Find character/subsequence | O(n·m) worst case | +| `substr()` | Create a substring | O(k) | +| `reversed()` | Return a reversed copy | O(n) | +| comparisons | Lexicographical comparison | O(min(n,m)) | + +`append()` uses geometric capacity growth, so repeated appends avoid an allocation on every operation. + +## Memory-management design + +Each `MyString` owns one character buffer and maintains three invariants: + +1. `data_[size_]` is always the null terminator. +2. `size_ <= capacity_`. +3. The allocated buffer contains at least `capacity_ + 1` characters. + +Copying allocates independent storage, so modifying a copy cannot mutate the source. Moving transfers the owned buffer without copying its characters; the source becomes an empty, reusable object. + +Copy assignment uses **copy-and-swap**, which avoids destroying the existing state before the replacement allocation succeeds. + +## Testing + +The test suite covers: + +- default and normal construction +- indexed access and `at()` exceptions +- deep copy semantics +- copy and move assignment +- moved-from object reuse +- capacity preservation after `clear()` +- self-append +- searching and substring boundaries +- lexicographical comparisons +- stream input/output +- embedded null characters + +Run it with: + ```bash -./MyString +cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug +cmake --build build --parallel +ctest --test-dir build --output-on-failure ``` -## Concepts Covered +## Why build this instead of using `std::string`? + +Because it is a useful systems-programming exercise. Implementing a small string type exposes concepts that are easy to hide behind the standard library: + +- RAII and ownership +- Rule of Five +- shallow vs deep copy +- move semantics +- exception safety +- dynamic allocation and reallocation +- object lifetime +- operator overloading +- API design and invariants +- algorithmic complexity -- Object-Oriented Programming -- Dynamic Memory Management -- Rule of Three -- Operator Overloading -- Pointers and Memory Safety +For application development, prefer the battle-tested standard library. For learning C++ internals, this project makes those mechanisms visible. + +## License + +MIT — see [`LICENSE`](LICENSE). ## Author -**Jyatin Singh** \ No newline at end of file +**Jyatin Kumar Singh** +C++ / Full-Stack Developer · Open Source Contributor + +GitHub: https://github.com/Jyatin From 0af986925c2c9d85f14a793dc8e732ae2b00ec59 Mon Sep 17 00:00:00 2001 From: Jyatin kumar Singh Date: Fri, 18 Sep 2026 03:55:16 +0530 Subject: [PATCH 13/18] docs: correct demo output and project structure --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index eb2fde3..9edcaeb 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ MyString/ │ └── workflows/ │ └── ci.yml # Build, test and sanitizer CI ├── CMakeLists.txt # Portable build configuration +├── LICENSE # MIT license └── README.md ``` @@ -80,8 +81,8 @@ The example exercises construction, concatenation, capacity, append, search, pre language : Modern C++17 project : MyString combined : Modern C++17 - MyString -size : 24 -capacity : 24 +size : 23 +capacity : 30 append : Modern C++17 - MyString library find : 17 contains : true @@ -97,7 +98,7 @@ cleared : "" (empty=true) === demo complete === ``` -The CI workflow builds this demo and runs the test suite on pushes and pull requests. It also runs the tests under AddressSanitizer and UndefinedBehaviorSanitizer on Ubuntu. +The CI workflow builds the demo and runs the test suite on pushes and pull requests. It also runs the tests under AddressSanitizer and UndefinedBehaviorSanitizer on Ubuntu. ## API at a glance @@ -118,7 +119,7 @@ The CI workflow builds this demo and runs the test suite on pushes and pull requ ## Memory-management design -Each `MyString` owns one character buffer and maintains three invariants: +Each `MyString` owns one character buffer and maintains these invariants: 1. `data_[size_]` is always the null terminator. 2. `size_ <= capacity_`. From ddf993de3d9e0c2503cade9c4727b000f2d739c8 Mon Sep 17 00:00:00 2001 From: Jyatin kumar Singh Date: Fri, 18 Sep 2026 03:55:21 +0530 Subject: [PATCH 14/18] chore: add MIT license --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0d7187f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Jyatin Kumar Singh + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 079978cc52e528bd50cf155dcadbc17ce54a1082 Mon Sep 17 00:00:00 2001 From: Jyatin kumar Singh Date: Fri, 18 Sep 2026 03:55:25 +0530 Subject: [PATCH 15/18] refactor: remove obsolete root demo --- Main.cpp | 100 ------------------------------------------------------- 1 file changed, 100 deletions(-) delete mode 100644 Main.cpp diff --git a/Main.cpp b/Main.cpp deleted file mode 100644 index 6baf13b..0000000 --- a/Main.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include -#include "MyString.h" - -using namespace std; - -int main() -{ - cout << "========== MyString Class Demonstration ==========\n\n"; - - MyString s1; - MyString s2; - - cout << "Enter first string : "; - cin >> s1; - - cout << "Enter second string: "; - cin >> s2; - - cout << "\n----- Input Strings -----\n"; - cout << "s1 = " << s1 << endl; - cout << "s2 = " << s2 << endl; - - cout << "\n----- Concatenation -----\n"; - MyString s3 = s1 + s2; - cout << "s1 + s2 = " << s3 << endl; - - cout << "\n----- Comparison -----\n"; - if (s1 == s2) - cout << "s1 and s2 are equal.\n"; - else - cout << "s1 and s2 are not equal.\n"; - - if (s1 != s2) - cout << "operator!= also works correctly.\n"; - else - cout << "operator!= also works correctly.\n"; - - cout << "\n----- Copy Constructor -----\n"; - MyString copy(s1); - - cout << "Original : " << s1 << endl; - cout << "Copied : " << copy << endl; - - cout << "\nChanging first character of original string...\n"; - - if (!s1.empty()) - s1[0] = 'X'; - - cout << "Original after modification : " << s1 << endl; - cout << "Copied remains : " << copy << endl; - - cout << "\nDeep copy verified.\n"; - - cout << "\n----- Assignment Operator -----\n"; - - MyString assign; - - assign = s2; - - cout << "Assigned string = " << assign << endl; - - cout << "\n----- Index Operator -----\n"; - - if (!assign.empty()) - { - cout << "First character : " << assign[0] << endl; - - assign[0] = 'Z'; - - cout << "Modified string : " << assign << endl; - } - - cout << "\n----- Utility Functions -----\n"; - - cout << "Length of s1 : " << s1.size() << endl; - cout << "Length of s2 : " << s2.size() << endl; - - if (s1.empty()) - cout << "s1 is empty.\n"; - else - cout << "s1 is not empty.\n"; - - cout << "\n----- Clear Function -----\n"; - - s2.clear(); - - cout << "After clear(), s2 = \"" << s2 << "\"" << endl; - cout << "Length = " << s2.size() << endl; - - if (s2.empty()) - cout << "s2 is now empty.\n"; - - cout << "\n----- c_str() Function -----\n"; - - cout << "Character array of s1 : " << s1.c_str() << endl; - - cout << "\n========== Program Finished Successfully ==========\n"; - - return 0; -} \ No newline at end of file From 18378df175130a5bf2d39e4d1245191f5a76f8e8 Mon Sep 17 00:00:00 2001 From: Jyatin kumar Singh Date: Fri, 18 Sep 2026 03:55:30 +0530 Subject: [PATCH 16/18] refactor: move header into include directory --- MyString.h | 42 ------------------------------------------ 1 file changed, 42 deletions(-) delete mode 100644 MyString.h diff --git a/MyString.h b/MyString.h deleted file mode 100644 index 7ef2f48..0000000 --- a/MyString.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef MYSTRING_H -#define MYSTRING_H - -#include - -using namespace std; - -class MyString -{ -private: - char* str; - int length; - - int stringLength(const char* s) const; - void copyString(char* dest, const char* src) const; - int compareString(const char* s1, const char* s2) const; - -public: - MyString(); - MyString(const char* s); - MyString(const MyString& other); - ~MyString(); - - MyString& operator=(const MyString& other); - - MyString operator+(const MyString& other) const; - bool operator==(const MyString& other) const; - bool operator!=(const MyString& other) const; - - char& operator[](int index); - const char& operator[](int index) const; - - int size() const; - bool empty() const; - void clear(); - const char* c_str() const; - - friend ostream& operator<<(ostream& out, const MyString& s); - friend istream& operator>>(istream& in, MyString& s); -}; - -#endif \ No newline at end of file From 56c51b56028ec0fd2c18d4742b3b839c918f5f4f Mon Sep 17 00:00:00 2001 From: Jyatin kumar Singh Date: Fri, 18 Sep 2026 03:55:34 +0530 Subject: [PATCH 17/18] refactor: move implementation into src directory --- MyString.cpp | 186 --------------------------------------------------- 1 file changed, 186 deletions(-) delete mode 100644 MyString.cpp diff --git a/MyString.cpp b/MyString.cpp deleted file mode 100644 index da39e50..0000000 --- a/MyString.cpp +++ /dev/null @@ -1,186 +0,0 @@ -#include "MyString.h" -#include -#include - -int MyString::stringLength(const char* s) const -{ - int len = 0; - - while (s[len] != '\0') - len++; - - return len; -} - -void MyString::copyString(char* dest, const char* src) const -{ - int i = 0; - - while (src[i] != '\0') - { - dest[i] = src[i]; - i++; - } - - dest[i] = '\0'; -} - -int MyString::compareString(const char* s1, const char* s2) const -{ - int i = 0; - - while (s1[i] != '\0' && s2[i] != '\0') - { - if (s1[i] != s2[i]) - return s1[i] - s2[i]; - - i++; - } - - return s1[i] - s2[i]; -} - -MyString::MyString() -{ - length = 0; - str = new char[1]; - str[0] = '\0'; -} - -MyString::MyString(const char* s) -{ - length = stringLength(s); - - str = new char[length + 1]; - - copyString(str, s); -} - -MyString::MyString(const MyString& other) -{ - length = other.length; - - str = new char[length + 1]; - - copyString(str, other.str); -} - -MyString::~MyString() -{ - delete[] str; -} - -MyString& MyString::operator=(const MyString& other) -{ - if (this != &other) - { - delete[] str; - - length = other.length; - - str = new char[length + 1]; - - copyString(str, other.str); - } - - return *this; -} - -MyString MyString::operator+(const MyString& other) const -{ - MyString temp; - - delete[] temp.str; - - temp.length = length + other.length; - - temp.str = new char[temp.length + 1]; - - int i; - - for (i = 0; i < length; i++) - temp.str[i] = str[i]; - - for (int j = 0; j < other.length; j++) - temp.str[i++] = other.str[j]; - - temp.str[i] = '\0'; - - return temp; -} - -bool MyString::operator==(const MyString& other) const -{ - return compareString(str, other.str) == 0; -} - -bool MyString::operator!=(const MyString& other) const -{ - return !(*this == other); -} - -char& MyString::operator[](int index) -{ - if (index < 0 || index >= length) - throw out_of_range("Index out of range"); - - return str[index]; -} - -const char& MyString::operator[](int index) const -{ - if (index < 0 || index >= length) - throw out_of_range("Index out of range"); - - return str[index]; -} - -int MyString::size() const -{ - return length; -} - -bool MyString::empty() const -{ - return length == 0; -} - -void MyString::clear() -{ - delete[] str; - - length = 0; - - str = new char[1]; - - str[0] = '\0'; -} - -const char* MyString::c_str() const -{ - return str; -} - -ostream& operator<<(ostream& out, const MyString& s) -{ - out << s.str; - - return out; -} - -istream& operator>>(istream& in, MyString& s) -{ - char buffer[1000]; - - in >> buffer; - - delete[] s.str; - - s.length = s.stringLength(buffer); - - s.str = new char[s.length + 1]; - - s.copyString(s.str, buffer); - - return in; -} \ No newline at end of file From 9df533eae1119a45d2bce4600e3655609a69a42c Mon Sep 17 00:00:00 2001 From: Jyatin kumar Singh Date: Fri, 18 Sep 2026 03:55:40 +0530 Subject: [PATCH 18/18] chore: improve build and IDE gitignore --- .gitignore | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 7f9497f..954e816 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,21 @@ -# Build files -*.exe -*.out +# Build artifacts +build/ +build-*/ +CMakeFiles/ +CMakeCache.txt +cmake_install.cmake +Makefile *.o *.obj +*.out +*.exe -# Visual Studio Code +# IDE files .vscode/ +.idea/ +*.user +*.suo -# Debug files +# Debug / compiler artifacts *.pdb *.ilk - -# CMake -CMakeFiles/ -CMakeCache.txt -cmake_install.cmake -Makefile