Giới thiệu
Bài viết này sẽ hướng dẫn cách tự xây dựng các hàm thành viên phổ biến của lớp String trong C++.
I. Các biến thành viên và hàm mặc định
1. Biến thành viên
class MyString {
private:
char* m_buffer; // Con trỏ lưu trữ chuỗi
size_t m_capacity; // Dung lượng bộ nhớ
size_t m_length; // Độ dài thực tế của chuỗi
static const size_t NIL; // Hằng số biểu diễn vị trí không tìm thấy
};
Khởi tạo hằng số tĩnh bên ngoài lớp:
const size_t MyString::NIL = static_cast<size_t>(-1);
2. Các hàm khởi tạo mặc định
a) Hàm khởi tạo có tham số
Triển khai hàm khởi tạo nhận vào chuỗi C-style:
// Khai báo
MyString(const char* src = "");
// Triển khai
MyString::MyString(const char* src)
{
m_length = std::strlen(src);
m_capacity = m_length;
m_buffer = new char[m_length + 1]; // Cấp phát thêm 1 byte cho '\0'
std::strcpy(m_buffer, src);
}
b) Hàm hủy
Giải phóng bộ nhớ đã cấp phát:
// Khai báo
~MyString();
// Triển khai
MyString::~MyString()
{
m_length = m_capacity = 0;
delete[] m_buffer;
m_buffer = nullptr;
}
c) Hàm sao chép (Copy Constructor)
Cần sử dụng deep copy vì có bộ nhớ động:
// Khai báo
MyString(const MyString& other);
// Triển khai
MyString::MyString(const MyString& other) : m_buffer(nullptr)
{
m_length = other.m_length;
m_capacity = other.m_capacity;
// Tạo đối tượng tạm và hoán đổi con trỏ
MyString temp(other.m_buffer);
std::swap(m_buffer, temp.m_buffer);
}
d) Toán tử gán
// Khai báo
MyString& operator=(MyString other);
// Triển khai
MyString& MyString::operator=(MyString other)
{
if (other.m_buffer == m_buffer)
return *this;
m_length = other.m_length;
m_capacity = other.m_capacity;
MyString temp(other.m_buffer);
std::swap(m_buffer, temp.m_buffer);
return *this;
}
II. Các hàm thành viên phổ biến
1. Hàm length
size_t length() const {
return m_length;
}
2. Hàm capacity
size_t capacity() const {
return m_capacity;
}
3. Hàm c_str
const char* c_str() const {
return m_buffer;
}
4. Hàm empty
bool empty() const {
return m_length == 0;
}
5. Hàm clear
void clear() {
m_buffer[0] = '\0';
m_length = 0;
}
6. Hàm reserve
Dự trữ bộ nhớ, chỉ tăng không giảm:
void reserve(size_t newCapacity)
{
if (m_capacity < newCapacity)
{
char* tmp = new char[newCapacity + 1];
std::strcpy(tmp, m_buffer);
delete[] m_buffer;
m_buffer = tmp;
m_capacity = newCapacity;
}
}
7. Hàm resize
Thay đổi kích thước và điền ký tự chỉ định:
void resize(size_t newSize, char fill = '\0')
{
if (newSize > m_length)
{
if (newSize > m_capacity)
reserve(newSize);
std::memset(m_buffer + m_length, fill, newSize - m_length);
}
m_length = newSize;
m_buffer[newSize] = '\0';
}
8. Hàm insert (chèn ký tự)
void insert(size_t pos, char ch)
{
assert(pos <= m_length);
if (m_length == m_capacity)
{
size_t newCap = m_capacity == 0 ? 4 : m_capacity * 2;
reserve(newCap);
}
int end = m_length + 1;
while (end >= static_cast<int>(pos))
{
m_buffer[end] = m_buffer[end - 1];
end--;
}
m_length++;
m_buffer[pos] = ch;
}
9. Hàm insert (chèn chuỗi)
void insert(size_t pos, const char* str)
{
assert(pos <= m_length);
size_t len = std::strlen(str);
if (m_length + len > m_capacity)
reserve(m_length + len);
for (int i = m_length; i >= static_cast<int>(pos); i--)
m_buffer[i + len] = m_buffer[i];
for (size_t i = 0; i < len; i++)
m_buffer[pos + i] = str[i];
m_length += len;
}
10. Hàm push_back
void push_back(char ch)
{
insert(m_length, ch);
}
11. Hàm append
void append(const char* str)
{
insert(m_length, str);
}
12. Hàm erase
void erase(size_t pos = 0, size_t count = NIL)
{
assert(pos < m_length);
if (count == NIL || count >= m_length - pos)
{
m_buffer[pos] = '\0';
m_length = pos;
}
else
{
for (size_t i = 0; i <= count; i++)
m_buffer[pos + i] = m_buffer[pos + count + i];
m_length -= count;
}
}
13. Hàm substr
MyString substr(size_t pos = 0, size_t count = NIL) const
{
assert(pos < m_length);
MyString result;
size_t end = pos + count;
if (count == NIL || count >= m_length - pos)
end = m_length;
for (size_t i = pos; i < end; i++)
result.push_back(m_buffer[i]);
return result;
}
14. Hàm find (tìm ký tự)
size_t find(char ch, size_t start = 0) const
{
for (size_t i = start; i < m_length; i++)
if (ch == m_buffer[i])
return i;
return NIL;
}
15. Hàm find (tìm chuỗi)
size_t find(const char* sub, size_t start = 0) const
{
char* p = std::strstr(m_buffer + start, sub);
if (!p) return NIL;
return p - m_buffer;
}
16. Hàm swap
void swap(MyString& other)
{
std::swap(m_buffer, other.m_buffer);
std::swap(m_length, other.m_length);
std::swap(m_capacity, other.m_capacity);
}
III. Nạp chồng toán tử
1. Các toán tử so sánh
bool operator<(const MyString& other) const
{
return std::strcmp(m_buffer, other.m_buffer) < 0;
}
bool operator==(const MyString& other) const
{
return std::strcmp(m_buffer, other.m_buffer) == 0;
}
bool operator<=(const MyString& other) const
{
return !(other < *this);
}
bool operator>(const MyString& other) const
{
return other < *this;
}
bool operator>=(const MyString& other) const
{
return !(*this < other);
}
bool operator!=(const MyString& other) const
{
return !(*this == other);
}
2. Toán tử +=
MyString& operator+=(char ch)
{
push_back(ch);
return *this;
}
MyString& operator+=(const char* str)
{
append(str);
return *this;
}
3. Toán tử []
char& operator[](size_t index)
{
assert(index < m_length);
return m_buffer[index];
}
const char& operator[](size_t index) const
{
assert(index < m_length);
return m_buffer[index];
}
4. Toán tử << và >>
std::ostream& operator<<(std::ostream& os, const MyString& s)
{
for (size_t i = 0; i < s.length(); i++)
os << s[i];
return os;
}
std::istream& operator>>(std::istream& is, MyString& s)
{
s.clear();
char ch = is.get();
while (ch != ' ' && ch != '\n')
{
s += ch;
ch = is.get();
}
return is;
}
IV. Iterator
Định nghĩa iterator để duyệt chuỗi:
typedef char* Iterator;
typedef const char* ConstIterator;
Iterator begin() { return m_buffer; }
Iterator end() { return m_buffer + m_length; }
ConstIterator begin() const { return m_buffer; }
ConstIterator end() const { return m_buffer + m_length; }
V. Mã nguồn hoàn chỉnh
MyString.h
#pragma once
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cassert>
namespace utils
{
class MyString
{
public:
typedef char* Iterator;
typedef const char* ConstIterator;
public:
MyString(const char* str = "");
~MyString();
MyString(const MyString& other);
MyString& operator=(MyString other);
MyString& operator+=(char ch);
MyString& operator+=(const char* str);
char& operator[](size_t idx);
const char& operator[](size_t idx) const;
bool operator<(const MyString& other) const;
bool operator<=(const MyString& other) const;
bool operator>(const MyString& other) const;
bool operator>=(const MyString& other) const;
bool operator==(const MyString& other) const;
bool operator!=(const MyString& other) const;
Iterator begin();
Iterator end();
ConstIterator begin() const;
ConstIterator end() const;
void insert(size_t pos, char ch);
void insert(size_t pos, const char* str);
void push_back(char ch);
void append(const char* str);
size_t find(char ch, size_t pos = 0) const;
size_t find(const char* sub, size_t pos = 0) const;
void clear();
void erase(size_t pos = 0, size_t count = NIL);
void swap(MyString& other);
const char* c_str() const;
size_t length() const;
size_t capacity() const;
bool empty() const;
void reserve(size_t newCapacity);
void resize(size_t newSize, char fill = '\0');
MyString substr(size_t pos = 0, size_t count = NIL) const;
private:
char* m_buffer;
size_t m_capacity;
size_t m_length;
static const size_t NIL;
};
std::ostream& operator<<(std::ostream& os, const MyString& s);
std::istream& operator>>(std::istream& is, MyString& s);
}
MyString.cpp
#include "MyString.h"
namespace utils
{
const size_t MyString::NIL = static_cast<size_t>(-1);
std::ostream& operator<<(std::ostream& os, const MyString& s)
{
for (size_t i = 0; i < s.length(); i++)
os << s[i];
return os;
}
std::istream& operator>>(std::istream& is, MyString& s)
{
s.clear();
char ch = is.get();
while (ch != ' ' && ch != '\n')
{
s += ch;
ch = is.get();
}
return is;
}
MyString::MyString(const char* str)
{
m_length = std::strlen(str);
m_capacity = m_length;
m_buffer = new char[m_length + 1];
std::strcpy(m_buffer, str);
}
MyString::~MyString()
{
m_length = m_capacity = 0;
delete[] m_buffer;
m_buffer = nullptr;
}
MyString::MyString(const MyString& other) : m_buffer(nullptr)
{
m_length = other.m_length;
m_capacity = other.m_capacity;
MyString temp(other.m_buffer);
std::swap(m_buffer, temp.m_buffer);
}
MyString& MyString::operator=(MyString other)
{
m_length = other.m_length;
m_capacity = other.m_capacity;
MyString temp(other.m_buffer);
std::swap(m_buffer, temp.m_buffer);
return *this;
}
MyString::Iterator MyString::begin() { return m_buffer; }
MyString::Iterator MyString::end() { return m_buffer + m_length; }
MyString::ConstIterator MyString::begin() const { return m_buffer; }
MyString::ConstIterator MyString::end() const { return m_buffer + m_length; }
MyString& MyString::operator+=(char ch)
{
push_back(ch);
return *this;
}
void MyString::push_back(char ch)
{
insert(m_length, ch);
}
MyString& MyString::operator+=(const char* str)
{
append(str);
return *this;
}
void MyString::append(const char* str)
{
insert(m_length, str);
}
bool MyString::empty() const { return m_length == 0; }
size_t MyString::length() const { return m_length; }
size_t MyString::capacity() const { return m_capacity; }
const char* MyString::c_str() const { return m_buffer; }
char& MyString::operator[](size_t idx)
{
assert(idx < m_length);
return m_buffer[idx];
}
const char& MyString::operator[](size_t idx) const
{
assert(idx < m_length);
return m_buffer[idx];
}
void MyString::clear()
{
m_buffer[0] = '\0';
m_length = 0;
}
void MyString::swap(MyString& other)
{
std::swap(m_buffer, other.m_buffer);
std::swap(m_length, other.m_length);
std::swap(m_capacity, other.m_capacity);
}
bool MyString::operator<(const MyString& other) const
{
return std::strcmp(m_buffer, other.m_buffer) < 0;
}
bool MyString::operator==(const MyString& other) const
{
return std::strcmp(m_buffer, other.m_buffer) == 0;
}
bool MyString::operator<=(const MyString& other) const
{
return !(other < *this);
}
bool MyString::operator>(const MyString& other) const
{
return other < *this;
}
bool MyString::operator>=(const MyString& other) const
{
return !(*this < other);
}
bool MyString::operator!=(const MyString& other) const
{
return !(*this == other);
}
size_t MyString::find(char ch, size_t pos) const
{
for (size_t i = pos; i < m_length; i++)
if (ch == m_buffer[i])
return i;
return NIL;
}
size_t MyString::find(const char* sub, size_t pos) const
{
char* p = std::strstr(m_buffer + pos, sub);
if (!p) return NIL;
return p - m_buffer;
}
void MyString::insert(size_t pos, char ch)
{
assert(pos <= m_length);
if (m_length == m_capacity)
{
size_t newCap = m_capacity == 0 ? 4 : m_capacity * 2;
reserve(newCap);
}
int end = m_length + 1;
while (end >= static_cast<int>(pos))
{
m_buffer[end] = m_buffer[end - 1];
end--;
}
m_length++;
m_buffer[pos] = ch;
}
void MyString::insert(size_t pos, const char* str)
{
assert(pos <= m_length);
size_t len = std::strlen(str);
if (m_length + len > m_capacity)
reserve(m_length + len);
for (int i = m_length; i >= static_cast<int>(pos); i--)
m_buffer[i + len] = m_buffer[i];
for (size_t i = 0; i < len; i++)
m_buffer[pos + i] = str[i];
m_length += len;
}
void MyString::erase(size_t pos, size_t count)
{
assert(pos < m_length);
if (count == NIL || count >= m_length - pos)
{
m_buffer[pos] = '\0';
m_length = pos;
}
else
{
for (size_t i = 0; i <= count; i++)
m_buffer[pos + i] = m_buffer[pos + count + i];
m_length -= count;
}
}
void MyString::reserve(size_t newCapacity)
{
if (m_capacity < newCapacity)
{
char* tmp = new char[newCapacity + 1];
std::strcpy(tmp, m_buffer);
delete[] m_buffer;
m_buffer = tmp;
m_capacity = newCapacity;
}
}
void MyString::resize(size_t newSize, char fill)
{
if (newSize > m_length)
{
if (newSize > m_capacity)
reserve(newSize);
std::memset(m_buffer + m_length, fill, newSize - m_length);
}
m_length = newSize;
m_buffer[newSize] = '\0';
}
MyString MyString::substr(size_t pos, size_t count) const
{
assert(pos < m_length);
MyString result;
size_t end = pos + count;
if (count == NIL || count >= m_length - pos)
end = m_length;
for (size_t i = pos; i < end; i++)
result.push_back(m_buffer[i]);
return result;
}
}