一、设计思路与核心特性
1.1 核心设计目标
- 内存安全:手动管理动态内存,避免内存泄漏、野指针、重复释放。
- 功能完备 :覆盖
std::string核心接口(构造、拷贝、拼接、比较、查找、替换等)。 - 性能优化 :
- 小字符串优化(SSO):短字符串直接存储在栈上,避免堆内存分配开销。
- 预分配内存(reserve):减少频繁扩容导致的内存拷贝。
- 移动语义(C++11+):转移资源所有权,避免拷贝开销。
- 兼容性 :支持与 C 风格字符串(
const char*)互操作,支持标准库算法(通过迭代器)。 - 鲁棒性:处理空指针、越界访问、内存分配失败等异常情况。
1.2 核心数据结构
- 采用 小字符串优化(SSO) 设计:
- 短字符串(长度 ≤ 15):存储在栈上的字符数组(避免堆分配)。
- 长字符串(长度 > 15):存储在堆上,通过指针管理,同时记录当前长度、容量。
- 成员变量:
- 联合(
union):复用内存,存储栈上字符数组或堆指针+长度+容量。 - 长度变量(
size_t size_):字符串实际长度(不含 '\0')。
- 联合(
二、完整实现代码
cpp
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstddef>
#include <stdexcept>
#include <utility> // 用于 std::move、std::swap
// 自定义 String 类,支持 SSO、移动语义、完整字符串操作
class MyString {
private:
// 小字符串优化(SSO)配置:栈上存储 15 个字符 + 1 个 '\0',共 16 字节(与 64 位指针对齐)
static constexpr size_t SSO_CAPACITY = 15;
// 联合:复用内存,存储栈上字符串或堆字符串信息
union Storage {
char sso_buf[SSO_CAPACITY + 1]; // 栈上缓冲区(+1 存储 '\0')
struct HeapData {
char* ptr; // 堆内存指针
size_t capacity; // 堆内存容量(不含 '\0')
} heap;
} storage_;
size_t size_; // 字符串实际长度(不含 '\0')
// 判断当前是否使用 SSO(栈存储)
bool isSSO() const noexcept {
return size_ <= SSO_CAPACITY;
}
// 获取当前容量(SSO 模式下容量固定为 15,堆模式下为 heap.capacity)
size_t capacity() const noexcept {
return isSSO() ? SSO_CAPACITY : storage_.heap.capacity;
}
// 堆模式下释放内存
void deallocate() noexcept {
if (!isSSO() && storage_.heap.ptr != nullptr) {
delete[] storage_.heap.ptr;
storage_.heap.ptr = nullptr;
}
}
// 确保容量至少为 new_cap(若不足则扩容,扩容策略:2倍增长)
void reserve(size_t new_cap) {
if (new_cap <= capacity()) return; // 容量足够,无需扩容
// 扩容后的容量:取 new_cap 和当前容量*2 的最大值(至少为 new_cap)
size_t new_capacity = std::max(new_cap, capacity() * 2);
char* new_ptr = new char[new_capacity + 1]; // +1 存储 '\0'
// 拷贝原字符串内容(含 '\0')
std::memcpy(new_ptr, c_str(), size_ + 1);
// 释放原堆内存(若为堆模式)
deallocate();
// 切换为堆模式,更新指针、容量
storage_.heap.ptr = new_ptr;
storage_.heap.capacity = new_capacity;
}
// 重新调整字符串长度(用于 resize、erase 等操作)
void resize(size_t new_size, char fill_char = '\0') {
if (new_size == size_) return;
// 若新长度大于当前容量,先扩容
if (new_size > capacity()) {
reserve(new_size);
}
// 填充新字符(若新长度大于原长度)
if (new_size > size_) {
std::memset(data() + size_, fill_char, new_size - size_);
}
// 更新长度,添加字符串结束符
size_ = new_size;
data()[size_] = '\0';
}
public:
// ========================= 构造函数 =========================
// 1. 默认构造:空字符串
MyString() noexcept : size_(0) {
storage_.sso_buf[0] = '\0'; // SSO 模式,空字符串
}
// 2. C 风格字符串构造
MyString(const char* cstr) {
if (cstr == nullptr) cstr = ""; // 处理空指针
size_ = std::strlen(cstr);
if (isSSO()) {
// SSO 模式:直接拷贝到栈缓冲区
std::memcpy(storage_.sso_buf, cstr, size_ + 1);
} else {
// 堆模式:分配内存并拷贝
storage_.heap.capacity = size_;
storage_.heap.ptr = new char[size_ + 1];
std::memcpy(storage_.heap.ptr, cstr, size_ + 1);
}
}
// 3. 字符串字面量构造(支持 "xxx"s 语法)
MyString(const char* cstr, size_t len) {
if (cstr == nullptr && len > 0) {
throw std::invalid_argument("cstr is nullptr but len > 0");
}
size_ = len;
if (isSSO()) {
std::memcpy(storage_.sso_buf, cstr, len);
storage_.sso_buf[len] = '\0'; // 手动添加结束符
} else {
storage_.heap.capacity = len;
storage_.heap.ptr = new char[len + 1];
std::memcpy(storage_.heap.ptr, cstr, len);
storage_.heap.ptr[len] = '\0';
}
}
// 4. 填充构造:n 个字符 ch 组成的字符串
MyString(size_t n, char ch) {
size_ = n;
if (isSSO()) {
std::memset(storage_.sso_buf, ch, n);
storage_.sso_buf[n] = '\0';
} else {
storage_.heap.capacity = n;
storage_.heap.ptr = new char[n + 1];
std::memset(storage_.heap.ptr, ch, n);
storage_.heap.ptr[n] = '\0';
}
}
// 5. 拷贝构造(深拷贝)
MyString(const MyString& other) {
size_ = other.size_;
if (other.isSSO()) {
// SSO 模式:直接拷贝栈缓冲区
std::memcpy(storage_.sso_buf, other.storage_.sso_buf, size_ + 1);
} else {
// 堆模式:分配新内存并拷贝内容(深拷贝)
storage_.heap.capacity = other.storage_.heap.capacity;
storage_.heap.ptr = new char[storage_.heap.capacity + 1];
std::memcpy(storage_.heap.ptr, other.storage_.heap.ptr, size_ + 1);
}
}
// 6. 移动构造(C++11+):转移资源所有权,避免拷贝
MyString(MyString&& other) noexcept : size_(other.size_) {
if (other.isSSO()) {
// SSO 模式:拷贝栈缓冲区(栈内存无法移动,只能拷贝)
std::memcpy(storage_.sso_buf, other.storage_.sso_buf, size_ + 1);
} else {
// 堆模式:转移指针所有权
storage_.heap = other.storage_.heap;
other.storage_.heap.ptr = nullptr; // 避免 other 析构时释放
}
other.size_ = 0; // 置空原对象
other.storage_.sso_buf[0] = '\0';
}
// ========================= 析构函数 =========================
~MyString() noexcept {
deallocate(); // 仅释放堆内存(SSO 模式无需处理)
}
// ========================= 赋值运算符 =========================
// 1. 拷贝赋值(深拷贝)
MyString& operator=(const MyString& other) {
if (this == &other) return *this; // 自赋值检查
MyString temp(other); // 拷贝构造临时对象(异常安全)
swap(temp); // 交换当前对象与临时对象的资源
return *this;
}
// 2. 移动赋值(C++11+)
MyString& operator=(MyString&& other) noexcept {
if (this == &other) return *this; // 自赋值检查
deallocate(); // 释放当前对象的堆内存
// 转移 other 的资源
size_ = other.size_;
if (other.isSSO()) {
std::memcpy(storage_.sso_buf, other.storage_.sso_buf, size_ + 1);
} else {
storage_.heap = other.storage_.heap;
other.storage_.heap.ptr = nullptr;
}
// 置空 other
other.size_ = 0;
other.storage_.sso_buf[0] = '\0';
return *this;
}
// 3. C 风格字符串赋值
MyString& operator=(const char* cstr) {
MyString temp(cstr); // 构造临时对象
swap(temp);
return *this;
}
// 4. 字符赋值
MyString& operator=(char ch) {
MyString temp(1, ch);
swap(temp);
return *this;
}
// ========================= 交换函数 =========================
void swap(MyString& other) noexcept {
if (this == &other) return;
// 交换大小
std::swap(size_, other.size_);
// 交换存储内容(根据存储模式选择交换方式)
if (isSSO() && other.isSSO()) {
// 均为 SSO 模式:交换栈缓冲区
char temp_buf[SSO_CAPACITY + 1];
std::memcpy(temp_buf, storage_.sso_buf, SSO_CAPACITY + 1);
std::memcpy(storage_.sso_buf, other.storage_.sso_buf, SSO_CAPACITY + 1);
std::memcpy(other.storage_.sso_buf, temp_buf, SSO_CAPACITY + 1);
} else if (!isSSO() && !other.isSSO()) {
// 均为堆模式:交换堆指针和容量
std::swap(storage_.heap.ptr, other.storage_.heap.ptr);
std::swap(storage_.heap.capacity, other.storage_.heap.capacity);
} else if (isSSO() && !other.isSSO()) {
// 当前为 SSO,other 为堆:将当前 SSO 内容转移到堆,再交换
char* temp_ptr = other.storage_.heap.ptr;
size_t temp_cap = other.storage_.heap.capacity;
// other 切换为 SSO 模式
std::memcpy(other.storage_.sso_buf, storage_.sso_buf, size_ + 1);
other.size_ = size_;
// 当前切换为堆模式
storage_.heap.ptr = temp_ptr;
storage_.heap.capacity = temp_cap;
size_ = other.size_;
} else {
// 当前为堆,other 为 SSO:调用 other 的交换逻辑
other.swap(*this);
}
}
// 全局 swap 函数(支持 std::swap)
friend void swap(MyString& lhs, MyString& rhs) noexcept {
lhs.swap(rhs);
}
// ========================= 基础访问接口 =========================
// 返回字符串长度(不含 '\0')
size_t size() const noexcept {
return size_;
}
// 返回字符串容量(最大可存储的字符数,不含 '\0')
size_t capacity() const noexcept {
return capacity();
}
// 判断是否为空字符串
bool empty() const noexcept {
return size_ == 0;
}
// 预分配内存(不改变字符串长度)
void reserve(size_t new_cap) {
reserve(new_cap);
}
// 收缩容量到实际长度(减少内存占用)
void shrink_to_fit() {
if (isSSO() || size_ == capacity()) return;
// 重新分配内存,容量 = 实际长度
char* new_ptr = new char[size_ + 1];
std::memcpy(new_ptr, c_str(), size_ + 1);
// 释放原内存
deallocate();
// 更新堆信息
storage_.heap.ptr = new_ptr;
storage_.heap.capacity = size_;
}
// 返回 C 风格字符串(const char*)
const char* c_str() const noexcept {
return isSSO() ? storage_.sso_buf : storage_.heap.ptr;
}
// 返回字符数据指针(非 const,可修改)
char* data() noexcept {
return isSSO() ? storage_.sso_buf : storage_.heap.ptr;
}
// 返回字符数据指针(const,不可修改)
const char* data() const noexcept {
return isSSO() ? storage_.sso_buf : storage_.heap.ptr;
}
// ========================= 字符访问 =========================
// 下标访问(非 const,无越界检查)
char& operator[](size_t idx) {
if (idx >= size_) {
throw std::out_of_range("MyString::operator[]: index out of range");
}
return data()[idx];
}
// 下标访问(const,无越界检查)
const char& operator[](size_t idx) const {
if (idx >= size_) {
throw std::out_of_range("MyString::operator[]: index out of range");
}
return data()[idx];
}
// 安全访问(at 方法,有越界检查,抛出异常)
char& at(size_t idx) {
if (idx >= size_) {
throw std::out_of_range("MyString::at: index out of range");
}
return data()[idx];
}
// 安全访问(const 版本)
const char& at(size_t idx) const {
if (idx >= size_) {
throw std::out_of_range("MyString::at: index out of range");
}
return data()[idx];
}
// 返回第一个字符
char& front() {
if (empty()) {
throw std::logic_error("MyString::front: empty string");
}
return data()[0];
}
const char& front() const {
if (empty()) {
throw std::logic_error("MyString::front: empty string");
}
return data()[0];
}
// 返回最后一个字符
char& back() {
if (empty()) {
throw std::logic_error("MyString::back: empty string");
}
return data()[size_ - 1];
}
const char& back() const {
if (empty()) {
throw std::logic_error("MyString::back: empty string");
}
return data()[size_ - 1];
}
// ========================= 字符串修改 =========================
// 清空字符串
void clear() noexcept {
deallocate(); // 释放堆内存
size_ = 0;
storage_.sso_buf[0] = '\0'; // 切换为 SSO 空字符串
}
// 调整字符串长度(填充字符)
void resize(size_t new_size, char fill_char = '\0') {
resize(new_size, fill_char);
}
// 在末尾添加一个字符
void push_back(char ch) {
if (size_ >= capacity()) {
reserve(size_ + 1); // 扩容
}
data()[size_] = ch;
size_++;
data()[size_] = '\0'; // 更新结束符
}
// 在末尾添加 C 风格字符串
void append(const char* cstr) {
if (cstr == nullptr) cstr = "";
size_t len = std::strlen(cstr);
if (len == 0) return;
// 确保容量足够
if (size_ + len > capacity()) {
reserve(size_ + len);
}
// 拷贝字符串
std::memcpy(data() + size_, cstr, len);
size_ += len;
data()[size_] = '\0';
}
// 在末尾添加指定长度的 C 风格字符串
void append(const char* cstr, size_t len) {
if (cstr == nullptr && len > 0) {
throw std::invalid_argument("cstr is nullptr but len > 0");
}
if (len == 0) return;
if (size_ + len > capacity()) {
reserve(size_ + len);
}
std::memcpy(data() + size_, cstr, len);
size_ += len;
data()[size_] = '\0';
}
// 在末尾添加另一个 MyString
void append(const MyString& other) {
append(other.data(), other.size());
}
// 在末尾添加 n 个字符 ch
void append(size_t n, char ch) {
if (n == 0) return;
if (size_ + n > capacity()) {
reserve(size_ + n);
}
std::memset(data() + size_, ch, n);
size_ += n;
data()[size_] = '\0';
}
// 插入字符到指定位置
MyString& insert(size_t pos, char ch) {
return insert(pos, 1, ch);
}
// 插入 n 个字符 ch 到指定位置
MyString& insert(size_t pos, size_t n, char ch) {
if (pos > size_) {
throw std::out_of_range("MyString::insert: pos out of range");
}
if (n == 0) return *this;
// 扩容
if (size_ + n > capacity()) {
reserve(size_ + n);
}
// 移动 pos 后的字符
std::memmove(data() + pos + n, data() + pos, size_ - pos);
// 填充新字符
std::memset(data() + pos, ch, n);
// 更新长度和结束符
size_ += n;
data()[size_] = '\0';
return *this;
}
// 插入 C 风格字符串到指定位置
MyString& insert(size_t pos, const char* cstr) {
if (cstr == nullptr) cstr = "";
return insert(pos, cstr, std::strlen(cstr));
}
// 插入指定长度的 C 风格字符串到指定位置
MyString& insert(size_t pos, const char* cstr, size_t len) {
if (pos > size_) {
throw std::out_of_range("MyString::insert: pos out of range");
}
if (cstr == nullptr && len > 0) {
throw std::invalid_argument("cstr is nullptr but len > 0");
}
if (len == 0) return *this;
// 扩容
if (size_ + len > capacity()) {
reserve(size_ + len);
}
// 移动 pos 后的字符
std::memmove(data() + pos + len, data() + pos, size_ - pos);
// 拷贝新字符串
std::memcpy(data() + pos, cstr, len);
// 更新长度和结束符
size_ += len;
data()[size_] = '\0';
return *this;
}
// 插入另一个 MyString 到指定位置
MyString& insert(size_t pos, const MyString& other) {
return insert(pos, other.data(), other.size());
}
// 删除指定位置开始的 n 个字符
MyString& erase(size_t pos = 0, size_t n = std::string::npos) {
if (pos >= size_) {
throw std::out_of_range("MyString::erase: pos out of range");
}
if (n == 0) return *this;
// 计算实际要删除的长度(不超过剩余字符数)
size_t erase_len = std::min(n, size_ - pos);
// 移动后续字符覆盖被删除部分
std::memmove(data() + pos, data() + pos + erase_len, size_ - pos - erase_len);
// 更新长度和结束符
size_ -= erase_len;
data()[size_] = '\0';
return *this;
}
// 替换指定位置的字符(替换 n 个字符为 ch)
MyString& replace(size_t pos, size_t n, size_t count, char ch) {
if (pos > size_) {
throw std::out_of_range("MyString::replace: pos out of range");
}
size_t erase_len = std::min(n, size_ - pos);
size_t add_len = count - erase_len;
// 若需要添加长度,先扩容
if (add_len > 0) {
if (size_ + add_len > capacity()) {
reserve(size_ + add_len);
}
// 移动后续字符
std::memmove(data() + pos + count, data() + pos + erase_len, size_ - pos - erase_len);
} else if (add_len < 0) {
// 若需要删除长度,直接移动后续字符
std::memmove(data() + pos + count, data() + pos + erase_len, size_ - pos - erase_len);
}
// 填充新字符
std::memset(data() + pos, ch, count);
// 更新长度
size_ += add_len;
data()[size_] = '\0';
return *this;
}
// 替换指定位置的字符为 C 风格字符串
MyString& replace(size_t pos, size_t n, const char* cstr) {
if (cstr == nullptr) cstr = "";
return replace(pos, n, cstr, std::strlen(cstr));
}
// 替换指定位置的字符为指定长度的 C 风格字符串
MyString& replace(size_t pos, size_t n, const char* cstr, size_t len) {
if (pos > size_) {
throw std::out_of_range("MyString::replace: pos out of range");
}
if (cstr == nullptr && len > 0) {
throw std::invalid_argument("cstr is nullptr but len > 0");
}
size_t erase_len = std::min(n, size_ - pos);
size_t add_len = len - erase_len;
// 扩容
if (add_len > 0 && size_ + add_len > capacity()) {
reserve(size_ + add_len);
}
// 移动后续字符
std::memmove(data() + pos + len, data() + pos + erase_len, size_ - pos - erase_len);
// 拷贝新字符串
std::memcpy(data() + pos, cstr, len);
// 更新长度
size_ += add_len;
data()[size_] = '\0';
return *this;
}
// 替换指定位置的字符为另一个 MyString
MyString& replace(size_t pos, size_t n, const MyString& other) {
return replace(pos, n, other.data(), other.size());
}
// ========================= 字符串拼接运算符 =========================
MyString operator+(const MyString& other) const {
MyString res(*this);
res.append(other);
return res;
}
MyString operator+(const char* cstr) const {
MyString res(*this);
res.append(cstr);
return res;
}
MyString operator+(char ch) const {
MyString res(*this);
res.push_back(ch);
return res;
}
friend MyString operator+(const char* cstr, const MyString& str) {
MyString res(cstr);
res.append(str);
return res;
}
friend MyString operator+(char ch, const MyString& str) {
MyString res(1, ch);
res.append(str);
return res;
}
// 复合赋值拼接
MyString& operator+=(const MyString& other) {
append(other);
return *this;
}
MyString& operator+=(const char* cstr) {
append(cstr);
return *this;
}
MyString& operator+=(char ch) {
push_back(ch);
return *this;
}
// ========================= 字符串比较 =========================
// 比较运算符(基于字典序)
bool operator==(const MyString& other) const noexcept {
if (size_ != other.size_) return false;
return std::memcmp(data(), other.data(), size_) == 0;
}
bool operator!=(const MyString& other) const noexcept {
return !(*this == other);
}
bool operator<(const MyString& other) const noexcept {
return std::memcmp(data(), other.data(), std::min(size_, other.size_)) < 0
|| (std::memcmp(data(), other.data(), std::min(size_, other.size_)) == 0 && size_ < other.size_);
}
bool operator<=(const MyString& other) const noexcept {
return *this < other || *this == other;
}
bool operator>(const MyString& other) const noexcept {
return !(*this <= other);
}
bool operator>=(const MyString& other) const noexcept {
return !(*this < other);
}
// 与 C 风格字符串比较
bool operator==(const char* cstr) const noexcept {
if (cstr == nullptr) cstr = "";
return std::strcmp(data(), cstr) == 0;
}
bool operator!=(const char* cstr) const noexcept {
return !(*this == cstr);
}
bool operator<(const char* cstr) const noexcept {
if (cstr == nullptr) cstr = "";
return std::strcmp(data(), cstr) < 0;
}
bool operator<=(const char* cstr) const noexcept {
return *this < cstr || *this == cstr;
}
bool operator>(const char* cstr) const noexcept {
return !(*this <= cstr);
}
bool operator>=(const char* cstr) const noexcept {
return !(*this < cstr);
}
friend bool operator==(const char* cstr, const MyString& str) noexcept {
return str == cstr;
}
friend bool operator!=(const char* cstr, const MyString& str) noexcept {
return str != cstr;
}
friend bool operator<(const char* cstr, const MyString& str) noexcept {
if (cstr == nullptr) cstr = "";
return std::strcmp(cstr, str.data()) < 0;
}
friend bool operator<=(const char* cstr, const MyString& str) noexcept {
return (cstr < str) || (cstr == str);
}
friend bool operator>(const char* cstr, const MyString& str) noexcept {
return !(cstr <= str);
}
friend bool operator>=(const char* cstr, const MyString& str) noexcept {
return !(cstr < str);
}
// 比较函数(返回 -1/0/1,兼容 C 风格)
int compare(const MyString& other) const noexcept {
int cmp = std::memcmp(data(), other.data(), std::min(size_, other.size_));
if (cmp != 0) return cmp;
return size_ - other.size_;
}
int compare(const char* cstr) const noexcept {
if (cstr == nullptr) cstr = "";
return std::strcmp(data(), cstr);
}
// ========================= 字符串查找 =========================
// 从 pos 开始查找字符 ch,返回第一次出现的位置(未找到返回 size_t(-1))
size_t find(char ch, size_t pos = 0) const noexcept {
if (pos >= size_) return std::string::npos;
const char* ptr = std::strchr(data() + pos, ch);
return ptr ? static_cast<size_t>(ptr - data()) : std::string::npos;
}
// 从 pos 开始查找 C 风格字符串,返回第一次出现的位置
size_t find(const char* cstr, size_t pos = 0) const noexcept {
if (cstr == nullptr || pos >= size_) return std::string::npos;
const char* ptr = std::strstr(data() + pos, cstr);
return ptr ? static_cast<size_t>(ptr - data()) : std::string::npos;
}
// 从 pos 开始查找 MyString,返回第一次出现的位置
size_t find(const MyString& other, size_t pos = 0) const noexcept {
if (other.empty() || pos >= size_) return std::string::npos;
return find(other.data(), pos);
}
// 从末尾开始查找字符 ch
size_t rfind(char ch, size_t pos = std::string::npos) const noexcept {
pos = std::min(pos, size_ - 1);
const char* ptr = std::strrchr(data(), ch);
return (ptr && static_cast<size_t>(ptr - data()) <= pos) ? static_cast<size_t>(ptr - data()) : std::string::npos;
}
// 从末尾开始查找 C 风格字符串
size_t rfind(const char* cstr, size_t pos = std::string::npos) const noexcept {
if (cstr == nullptr || empty()) return std::string::npos;
size_t cstr_len = std::strlen(cstr);
if (cstr_len == 0) return std::min(pos, size_);
if (cstr_len > size_) return std::string::npos;
pos = std::min(pos, size_ - cstr_len);
for (size_t i = pos; ; --i) {
if (std::memcmp(data() + i, cstr, cstr_len) == 0) {
return i;
}
if (i == 0) break;
}
return std::string::npos;
}
// 从末尾开始查找 MyString
size_t rfind(const MyString& other, size_t pos = std::string::npos) const noexcept {
if (other.empty()) return std::min(pos, size_);
return rfind(other.data(), pos);
}
// ========================= 子字符串 =========================
// 返回从 pos 开始、长度为 len 的子字符串(len 为 npos 时取到末尾)
MyString substr(size_t pos = 0, size_t len = std::string::npos) const {
if (pos >= size_) {
throw std::out_of_range("MyString::substr: pos out of range");
}
size_t actual_len = std::min(len, size_ - pos);
return MyString(data() + pos, actual_len);
}
// ========================= 输入输出运算符 =========================
friend std::ostream& operator<<(std::ostream& os, const MyString& str) {
os << str.c_str();
return os;
}
friend std::istream& operator>>(std::istream& is, MyString& str) {
char buf[1024]; // 临时缓冲区(可根据需求调整大小)
is >> buf; // 读取到空格/换行结束
str = buf; // 赋值给 MyString
return is;
}
// 读取一行(包括空格,直到换行)
friend std::istream& getline(std::istream& is, MyString& str) {
char buf[1024];
is.getline(buf, sizeof(buf));
str = buf;
return is;
}
// ========================= 迭代器支持(兼容标准库算法) =========================
using iterator = char*;
using const_iterator = const char*;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
// 正向迭代器
iterator begin() noexcept { return data(); }
const_iterator begin() const noexcept { return data(); }
const_iterator cbegin() const noexcept { return data(); }
iterator end() noexcept { return data() + size_; }
const_iterator end() const noexcept { return data() + size_; }
const_iterator cend() const noexcept { return data() + size_; }
// 反向迭代器
reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); }
const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(end()); }
reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); }
const_reverse_iterator crend() const noexcept { return const_reverse_iterator(begin()); }
};
// 全局 swap 函数(必须在命名空间内,支持 ADL)
inline void swap(MyString& lhs, MyString& rhs) noexcept {
lhs.swap(rhs);
}
三、核心功能解析
3.1 小字符串优化(SSO)
- 设计原理:大多数字符串是短字符串(如用户名、关键词),直接存储在栈上可避免堆内存分配的开销(堆分配需系统调用,耗时且有内存碎片)。
- 实现细节 :
- 用
union复用内存:栈缓冲区(16 字节)和堆指针+容量(16 字节,64 位系统指针占 8 字节,size_t占 8 字节)占用相同内存空间。 - 长度 ≤ 15 时使用栈缓冲区,否则使用堆内存。
- 用
3.2 拷贝控制(深拷贝+移动语义)
- 深拷贝:拷贝构造/赋值时,堆模式下分配新内存并拷贝内容,避免多个对象共享同一块堆内存(导致重复释放)。
- 移动语义:移动构造/赋值时,直接转移堆内存的所有权(仅拷贝指针),原对象置空,避免拷贝开销(适用于临时对象)。
- 异常安全 :赋值运算符采用"拷贝-交换"技术(
copy-and-swap),确保赋值过程中若抛出异常(如内存分配失败),当前对象状态不变。
3.3 内存管理
- 扩容策略:当容量不足时,按当前容量的 2 倍扩容(平衡内存占用和拷贝开销)。
- 内存释放:析构函数仅释放堆内存,栈内存自动回收。
- 收缩内存 :
shrink_to_fit()方法将容量收缩到实际长度,减少内存浪费。
3.4 字符串操作
- 基础操作 :
size()/empty()/clear()/reserve()/resize()等,与std::string接口一致。 - 修改操作 :
push_back()/append()/insert()/erase()/replace(),支持字符、C 风格字符串、MyString三种参数类型。 - 比较操作 :重载所有比较运算符(
==/!=/</<=/>/>=),支持与MyString和const char*比较,基于字典序。 - 查找操作 :
find()/rfind()支持字符和字符串查找,返回第一次/最后一次出现的位置。 - 子字符串 :
substr()提取指定范围的子字符串。
3.5 迭代器支持
- 实现正向迭代器、反向迭代器,兼容 C++ 标准库算法(如
std::sort、std::find)。 - 迭代器直接指向字符数据(栈缓冲区或堆指针),操作高效。
3.6 兼容性
- 支持与 C 风格字符串(
const char*)互操作(构造、赋值、比较、拼接)。 - 重载输入输出运算符(
<</>>)和getline(),支持标准输入输出。
四、使用示例
cpp
#include <algorithm> // 用于 std::sort
int main() {
// 1. 构造函数
MyString s1; // 空字符串
MyString s2("hello"); // C 风格字符串构造
MyString s3(3, 'a'); // 3 个 'a':"aaa"
MyString s4(s2); // 拷贝构造
MyString s5(std::move(s3)); // 移动构造(s3 变为空)
std::cout << "s1: " << s1 << "(size=" << s1.size() << ")" << std::endl;
std::cout << "s2: " << s2 << "(size=" << s2.size() << ")" << std::endl;
std::cout << "s3: " << s3 << "(size=" << s3.size() << ")" << std::endl;
std::cout << "s4: " << s4 << "(size=" << s4.size() << ")" << std::endl;
std::cout << "s5: " << s5 << "(size=" << s5.size() << ")" << std::endl;
// 2. 赋值与拼接
s1 = "world";
s1 += "!"; // 拼接 C 风格字符串
s1 += ' '; // 拼接字符
s1 += s2; // 拼接 MyString
std::cout << "\ns1 after append: " << s1 << std::endl; // "world! hello"
MyString s6 = s2 + " " + s1; // 混合拼接
std::cout << "s6: " << s6 << std::endl; // "hello world! hello"
// 3. 字符访问
std::cout << "\ns6[0]: " << s6[0] << std::endl; // 'h'
std::cout << "s6.at(5): " << s6.at(5) << std::endl; // ' '
std::cout << "s6.front(): " << s6.front() << std::endl; // 'h'
std::cout << "s6.back(): " << s6.back() << std::endl; // 'o'
// 4. 字符串修改
s6.insert(5, "beautiful "); // 插入字符串
std::cout << "\ns6 after insert: " << s6 << std::endl; // "hello beautiful world! hello"
s6.erase(5, 10); // 删除 10 个字符
std::cout << "s6 after erase: " << s6 << std::endl; // "hello world! hello"
s6.replace(5, 1, ""); // 替换为空(删除)
std::cout << "s6 after replace: " << s6 << std::endl; // "helloworld! hello"
// 5. 查找与子字符串
size_t pos = s6.find("world");
if (pos != MyString::npos) {
std::cout << "\nfind 'world' at pos: " << pos << std::endl; // 5
MyString sub = s6.substr(pos, 5);
std::cout << "substring: " << sub << std::endl; // "world"
}
// 6. 比较
std::cout << "\n s2 == 'hello': " << (s2 == "hello") << std::endl; // true
std::cout << " s2 > 'hell': " << (s2 > "hell") << std::endl; // true
// 7. 迭代器与标准库算法
std::sort(s6.begin(), s6.end()); // 排序字符串
std::cout << "\ns6 after sort: " << s6 << std::endl; // " !dehllloorw"
// 8. 内存管理
std::cout << "\ns6 capacity: " << s6.capacity() << std::endl;
s6.shrink_to_fit();
std::cout << "s6 capacity after shrink: " << s6.capacity() << std::endl;
// 9. 输入输出
MyString s7;
std::cout << "\nenter a string: ";
std::cin >> s7;
std::cout << "you entered: " << s7 << std::endl;
return 0;
}
五、关键注意事项
5.1 内存安全
- 禁止直接访问
data()指针并修改超出size_的内存(可能破坏字符串结束符'\0')。 - 避免使用已移动的
MyString对象(移动后对象为空,仅可赋值或析构)。 - 处理空指针:所有接收
const char*的接口均会处理空指针(视为空字符串),避免崩溃。
5.2 异常处理
- 越界访问(
at()/insert()/erase()等)会抛出std::out_of_range异常,建议捕获处理。 - 内存分配失败会抛出
std::bad_alloc异常(C++ 默认行为)。
5.3 性能优化
- 频繁拼接字符串时,先调用
reserve()预分配足够容量,减少扩容次数。 - 短字符串自动使用 SSO,无需手动优化;长字符串建议合理设置初始容量。
- 移动语义仅对临时对象生效(如函数返回值),若需转移左值对象的所有权,需用
std::move()显式转换。
5.4 与 std::string 的区别
- 本实现的 SSO 容量为 15(
std::string通常为 15 或 22,依赖编译器)。 - 未实现
std::string的所有接口(如find_first_of/find_last_not_of等),可根据需求扩展。 - 不支持宽字符(
wchar_t),若需支持可修改为模板类(MyString<T>)。
六、扩展方向
- 支持宽字符 :将类改为模板类
MyString<T>,支持char和wchar_t。 - 增加字符串处理函数 :如
to_upper()/to_lower()/trim()/split()等。 - 支持正则表达式 :集成
std::regex实现正则匹配、替换。 - 内存池优化:对于频繁创建/销毁的字符串,使用内存池管理堆内存,减少内存碎片。
- 线程安全:添加互斥锁,支持多线程并发访问(需权衡性能)。
通过手动实现 MyString 类,可深入理解 C++ 的内存管理、拷贝控制、面向对象设计等核心知识点,同时掌握字符串操作的底层原理,为使用 std::string 或其他容器提供理论支撑。