C++ 自定义 String 类

一、设计思路与核心特性

1.1 核心设计目标

  1. 内存安全:手动管理动态内存,避免内存泄漏、野指针、重复释放。
  2. 功能完备 :覆盖 std::string 核心接口(构造、拷贝、拼接、比较、查找、替换等)。
  3. 性能优化
    • 小字符串优化(SSO):短字符串直接存储在栈上,避免堆内存分配开销。
    • 预分配内存(reserve):减少频繁扩容导致的内存拷贝。
    • 移动语义(C++11+):转移资源所有权,避免拷贝开销。
  4. 兼容性 :支持与 C 风格字符串(const char*)互操作,支持标准库算法(通过迭代器)。
  5. 鲁棒性:处理空指针、越界访问、内存分配失败等异常情况。

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 三种参数类型。
  • 比较操作 :重载所有比较运算符(==/!=/</<=/>/>=),支持与 MyStringconst char* 比较,基于字典序。
  • 查找操作find()/rfind() 支持字符和字符串查找,返回第一次/最后一次出现的位置。
  • 子字符串substr() 提取指定范围的子字符串。

3.5 迭代器支持

  • 实现正向迭代器、反向迭代器,兼容 C++ 标准库算法(如 std::sortstd::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>)。

六、扩展方向

  1. 支持宽字符 :将类改为模板类 MyString<T>,支持 charwchar_t
  2. 增加字符串处理函数 :如 to_upper()/to_lower()/trim()/split() 等。
  3. 支持正则表达式 :集成 std::regex 实现正则匹配、替换。
  4. 内存池优化:对于频繁创建/销毁的字符串,使用内存池管理堆内存,减少内存碎片。
  5. 线程安全:添加互斥锁,支持多线程并发访问(需权衡性能)。

通过手动实现 MyString 类,可深入理解 C++ 的内存管理、拷贝控制、面向对象设计等核心知识点,同时掌握字符串操作的底层原理,为使用 std::string 或其他容器提供理论支撑。

相关推荐
froginwe111 小时前
CSS Text(文本)详解
开发语言
phdsky1 小时前
【设计模式】工厂方法模式
c++·设计模式·工厂方法模式
龙仔7251 小时前
实现分布式读写集群(提升两台服务器的性能,支持分片存储+并行读写),Redis Cluster(Redis集群模式)并附排错过程
服务器·redis·分布式
n***4431 小时前
Java进阶:IO大全
java·开发语言·python
jtymyxmz1 小时前
《JavaEE企业级应用开发教程(Spring+Spring MVC+Mybatis)》3.2 动态代理
java·开发语言
dishugj1 小时前
Linux系统磁盘性能相关命令详解与实例分析
linux·运维·服务器
喵霓1 小时前
mac系统的环境变量-bash_profile
开发语言·macos·bash
向葭奔赴♡1 小时前
Android SharedPreferences实战指南
android·java·开发语言
6***A6631 小时前
SQL 插入数据详解
服务器·数据库·sql