跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
C++算法

C++ STL string 类从零实现详解

C++ string 类从零实现涵盖内存管理、迭代器及运算符重载。通过自定义命名空间避免冲突,采用深拷贝策略处理构造与赋值防止泄漏。实现包括动态内存分配、容量扩容、字符串修改及访问接口。完成比较运算符与流输入输出重载,模拟标准库行为并优化性能。

清心发布于 2026/3/26更新于 2026/5/67 浏览
C++ STL string 类从零实现详解

一、string 类总览

为了避免与标准库中的 string 产生命名冲突,使用 mystd 命名空间进行封装。

namespace mystd {
class string {
public:
    //迭代器
    typedef char* iterator;
    typedef const char* const_iterator;

    //默认成员函数
    string();
    string(const char* str);
    string(const string& s); //拷贝构造函数
    string& operator=(const string& s); //赋值运算符重载函数
    ~string(); //析构函数

    //迭代器相关函数
    iterator begin();
    iterator end();
    const_iterator begin() const;
    const_iterator end() const;

    //容量和大小相关函数
    size_t size();
    size_t capacity();
    void reserve(size_t n);
    ;
    ;

    
    ;
    ;
    string& +=( ch);
    string& +=( * str);
    ;
    ;
    ;
    ;
    ;
    ;

    
    & []( i);
     & []( i) ;
    ;
    ;
    ;
    ;

:
    * _str; 
     _size; 
     _capacity; 
       npos; 
};

  string::npos = ;


 <( string& s1,  string& s2);
 <=( string& s1,  string& s2);
 >( string& s1,  string& s2);
 >=( string& s1,  string& s2);
 ==( string& s1,  string& s2);
 !=( string& s1,  string& s2);


ostream& <<(ostream& out,  string& s);
istream& >>(istream& in, string& s);
}
void resize(size_t n, char ch = '\0')
bool empty() const
//修改字符串相关函数
void push_back(char ch)
void append(const char* str)
operator
char
operator
const
char
string& insert(size_t pos, char ch)
string& insert(size_t pos, const char* str)
string& erase(size_t pos, size_t len)
void clear()
void swap(string& s)
const char* c_str() const
//访问字符串相关函数
char
operator
size_t
const
char
operator
size_t
const
size_t find(char ch, size_t pos = 0) const
size_t find(const char* str, size_t pos = 0) const
size_t rfind(char ch, size_t pos = npos) const
size_t rfind(const char* str, size_t pos = 0) const
private
char
//存储字符串
size_t
//记录字符串当前的有效长度
size_t
//记录字符串当前的容量
static
const
size_t
//静态成员变量(整型最大值)
const
size_t
-1
//关系运算符重载函数
bool
operator
const
const
bool
operator
const
const
bool
operator
const
const
bool
operator
const
const
bool
operator
const
const
bool
operator
const
const
//<<和>>运算符重载函数
operator
const
operator

成员变量分析

  1. char* _str:用来存储字符串。
  2. size_t _size:记录当前字符串的长度(不包含'\0')。
  3. size_t _capacity:记录字符串当前的容量(不包含'\0')。
  4. static const size_t npos:这是一个静态成员变量,其值为 -1(即整型的最大值),通常用作函数的异常返回值。

迭代器分析

  1. typedef char* iterator:通过重命名 char* 指针作为迭代器。
  2. typedef const char* const_iterator:通过重命名 const char* 指针作为常量迭代器。

二、默认成员函数

2.1 无参构造函数

//1. string 的无参构造
string() : _str(new char[1] {'\0'}), _size(0), _capacity(0) {}

代码分析:

  1. 默认 string 类预留一个'\0'作为空字符的表示。
  2. 初始化 _size 为 0。
  3. 初始化 _capacity 为 0。 注:空字符的字符个数 _size 和空间 _capacity 均为 0,即'\0'额外单独开空间。

2.2 带参构造函数

//2. string 的带参构造
string(const char* str) {
    //计算字符串的字符个数
    _size = strlen(str);
    //温馨提示:_capacity 所开辟的空间也不包含\0
    _capacity = _size;
    //单独为'\0'开空间,所以开辟空间_capacity+1
    _str = new char[_capacity + 1];
    //拷贝 C 类型字符
    strcpy(_str, str);
}

温馨提示:

  1. 使用初始化列表进行构造时,初始化顺序是按照成员变量声明顺序执行的。
  2. 为了复用 strlen 计算结果,不建议在此处使用初始化列表方式。

2.3 拷贝构造函数

在模拟实现拷贝构造函数前,应该首先了解深浅拷贝:

  1. 浅拷贝:拷贝出来的目标对象的指针和源对象的指针指向的内存空间是同一块空间,其中一个对象的改动会对另一个对象造成影响。
  2. 深拷贝:深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动不会对另外一个对象造成影响。

分析:string 类需要深拷贝的原因

  1. 每个 string 类对象需要独立的空间进行存储字符串,要求任何一个对象的改动不会对另外一个对象造成影响,因此 str1 拷贝 str2 需要进行深拷贝。
  2. 深拷贝给每个对象独立分配资源,保证多个对象之间不会因共享资源而造成多次释放造成程序崩溃问题。

代码一:传统写法

string(const string& s) : _size(s._size), _capacity(s._size) // 容量按需分配,节省内存
{
    _str = new char[_capacity + 1];
    strcpy(_str, s._str);
}

例如:string str2(str1)

  1. 首先为拷贝对象 str2 分配足够容纳源对象 str1 的内存空间。
  2. 然后将源对象 str1 的字符串内容完整复制到 str2 中。
  3. 最后将源对象 str1 的所有成员变量值赋给 str2。 注意:由于 str2 的 _str 指针和 str1 的 _str 指针指向不同的内存空间,因此这是一个深拷贝操作,两个对象完全独立。

代码二:现代写法

//封装 swap 函数:用来交换成员变量
void swap(string& tmp) {
    //调用库函数中的 swap 进行交换两数
    std::swap(_str, tmp._str);
    std::swap(_size, tmp._size);
    std::swap(_capacity, tmp._capacity);
}

// 现代写法 copy-and-swap 写法
string(const string& s) {
    string tmp(s._str);
    swap(tmp);
}

代码解析: 场景演示:假设执行 string s2(s1);

  1. s1 传引用传参给 s,即 s 为 s1 的别名。
  2. 调用构造函数,构造出一个 C 字符串为 s._str 的对象 tmp。
  3. tmp 由带参构造函数而来,字符串指向的地址和 s 不同,但字符串个数 _size 和容量 _capacity 与 s1 完全相同。
  4. 再将 s2 和 tmp 进行交换,此时 s2 的值就与 s1 的值相同,于此时同时 tmp 的值为原来的 s2,函数结束时进行析构原来的 s2 指向的空间。

温馨提示: 如果待拷贝对象 s2 的成员变量:char* _str、size_t _size、size_t _capacity A. 没有在类内给缺省值(比如 = nullptr) B. 也没有在拷贝构造函数的初始化列表中被初始化 在执行 swap(tmp, s2) 时,由于 s2 指向的是随机内存空间,交换后当 tmp 调用析构函数时会导致严重错误。

2.4 赋值运算符重载

赋值运算符重载与拷贝构造函数类似,同样需要考虑深浅拷贝问题。

为了实现深拷贝,提供以下两种实现方式:

① 传统写法

//例如:s2 = s1
//返回类型为 string& 实现了连续的赋值,并且减少了拷贝
string& operator=(const string& other) {
    if (this == &other) {
        return *this;
    }
    delete[] _str;
    _str = new char[other._capacity + 1];
    strcpy(_str, other._str);
    _size = other._size;
    _capacity = other._capacity;
    return *this;
}

赋值运算符的几个关键步骤:

  1. 防范自我赋值(if (this == &other)):防止 s1 = s1 时,先把自己的内存释放了,导致后面的拷贝出现段错误。
  2. 清理旧资源(delete[] _str):防止内存泄漏。
  3. 深拷贝新资源(new 和 strcpy)。
  4. 返回自身引用(return *this):支持 s1 = s2 = s3 这样的链式操作。

② 现代写法

//[补充] 实现赋值重载:copy-and-swap 写法
/* 假设执行 s2=s1; s1 传值传参给 tmp,此时 tmp 执行深拷贝构造
   再将 s2 和 tmp 进行交换,此时 s2 的值就与 s1 的值相同,于此时同时 tmp 的值为原来的 s2,函数结束时进行析构原来的 s2 指向的空间 */
//封装 swap 函数
void swap(string& tmp) {
    std::swap(_str, tmp._str);
    std::swap(_size, tmp._size);
    std::swap(_capacity, tmp._capacity);
}

string& operator=(string tmp) {
    swap(tmp);
    return *this;
}

场景示例:假设你写了这句代码:s1 = s2;

  1. 第 1 步:利用参数'按值传递',外包【深拷贝】 发生了什么:当进入 operator= 函数时,因为参数是按值传递的(string tmp),C++ 编译器会自动调用'拷贝构造函数',用 s2 克隆出一个局部的临时对象 tmp。 为什么高明:这个临时对象 tmp 已经是一份完美的深拷贝了!此时 s1 毫发无损,完美解决了我们之前担心的'旧家拆了,新家没建好'的致命隐患。
  2. 第 2 步:偷天换日,外包 发生了什么:进入函数体后,s1(即 this)看着临时对象 tmp 手里拿着新鲜出炉的数据,于是执行了 std::swap,直接把两者的底牌(指针、容量、大小)互换了。 结果:*s1 拿到了 tmp 申请好的新内存和新数据(更新完成!)。tmp 成了一个'背锅侠',手里拿着 s1 淘汰下来的旧内存和旧数据。
  3. 第 3 步:借刀杀人,外包【旧内存释放】 发生了什么:随着 return *this; 执行完毕,函数结束。 为什么高明:局部对象 tmp 的生命周期走到了尽头。C++ 编译器会自动调用它的'析构函数',因为此时 tmp 手里捏着的是 s1 的旧内存,析构函数手起刀落,顺道就把 s1 之前的旧垃圾清理得干干净净!完全不需要手动写 delete[]。

2.5 析构函数

//析构函数
~string() {
    delete[] _str;
    _size = 0;
    _capacity = 0;
}

三、迭代器

string 类中的迭代器本质上是字符指针,iterator 只是其类型别名。

typedef char* iterator;
typedef const char* const_iterator;

3.1 begin

string 类中的 begin 函数:返回字符串首字符的地址,即迭代器初始的位置。

//A. 普通'读写版'
iterator begin() {
    return _str;
}
//B. 常量'只读版'
const_iterator begin() const {
    return _str;
}

3.2 end

string 类中的 end 函数:返回字符串中最后一个字符的后一个字符的地址(即'\0'的地址),即迭代器的末位置。

//A. 普通'读写版'
iterator end() {
    return _str + _size;
}
//B. 常量'只读版'
const_iterator end() const {
    return _str + _size;
}

四、容量和大小相关函数

4.1 size

由于 string 类的成员变量是私有的,无法直接访问,因此它提供了 size() 这个成员函数,用于获取字符串的字符个数。

//返回字符串的长度
size_t size() const {
    return _size;
}

4.2 capacity

由于 string 类的成员变量是私有的,无法直接访问,因此它提供了 capacity() 这个成员函数,用于获取字符串的容量个数。

//返回字符串容量的函数
size_t capacity() const {
    return _capacity;
}

4.3 reserve

reserve 函数:

  • 函数原型为:void reserve(size_t n);
  • 函数功能为:
    1. 当 n 超过对象当前 capacity 时,将 capacity 扩展至不小于 n 的大小。
    2. 当 n 小于当前 capacity 时,维持现有 capacity 不变。

代码示例:

//reserve 函数的实现:对字符串进行扩容操作,参数 size_t n 表示新容量的大小
//温馨提示:一般只扩容不缩容
void string::reserve(size_t n) {
    if (n > _capacity) {
        //注意:预留一个位置给'\0',capacity 的大小不包含'\0'
        char* tmp = new char[n + 1];
        //拷贝旧空间的值到新空间
        strcpy(tmp, _str);
        //释放旧空间
        delete[] _str;
        //指向新空间
        _str = tmp;
        //更新当前空间
        _capacity = n;
    }
}

4.4 empty

代码示例:判断字符串是否为空,调用 strcmp 库函数进行实现

bool string::empty() {
    return strcmp(_str, "") == 0;
}

五、字符串修改函数

5.1 push_back

push_back 函数:

  • 函数原型为:void push_back(char c);
  • 函数功能为:在当前字符串的后面尾插上一个字符。

代码实现:

// push_back 函数的实现:尾插一个字符
void string::push_back(char c) {
    //判定是否能够进行尾插,检查容量是否足够
    if (_size == _capacity) {
        //使用二倍扩容法
        reserve(_capacity == 0 ? 4 : _capacity * 2);
    }
    _str[_size++] = c;
    //注意:_str[_size] 的'\0'被插入的字符 c 所覆盖,所以需要再补充一个'\0'
    _str[_size] = '\0';
}

代码讲解:

  1. 在尾插操作前,首先需要检查当前容量是否足够。如果空间不足,则调用 reserve 函数进行扩容。
  2. 完成扩容检查后,执行字符的尾插操作。
  3. 特别需要注意的是,尾插字符后必须在其后添加'\0'终止符。否则在打印字符串时可能出现非法访问问题,因为新插入字符的后方位置不一定存在字符串终止符。

5.2 append

append 函数:

  • 函数原型:void append(const char* str)
  • 函数功能:在当前字符串的后面尾插一个字符串。

代码示例:

// append 函数的实现:尾插一个字符串
void string::append(const char* s) {
    //判断是否能够进行尾插入一个字符串
    size_t len = strlen(s);
    if (len + _size > _capacity) {
        //对字符串进行扩容操作
        //扩容规则:大于当前字符串长度的二倍,按照所需长度扩容,反之按照当前容量的二倍扩容,进而减少扩容的次数
        size_t newcapacity = (_size + len > _capacity * 2) ? _size + len : _capacity * 2;
        reserve(newcapacity);
    }
    //通过 strcpy,从源字符串'\0'的位置即:_str+_size,尾插字符串 s
    strcpy(_str + _size, s);
    //更新当前 size 的长度
    _size += len;
}

代码详解:

  1. 在执行尾插操作前,需先检查当前字符串缓冲区是否具备足够空间。
  2. 若空间不足,则需先进行扩容操作。
  3. 扩容完成后,将待插入字符串追加至目标字符串末尾。 注意:由于待插入字符串本身已包含终止符'\0',故无需额外添加。

5.3 operator+=

operator+= 函数重载:

  • A. 函数原型:
    1. string& operator+=(const char* s);
    2. string& operator+=(const char c);
  • B. 函数功能:
    1. 重载运算符+= 使其能够通过运算符尾插一个字符串。
    2. 重载运算符+= 使其能够通过运算符尾插一个字符。
  • C. 函数实现思想:
    1. 对于字符串与字符的尾插,直接调用 push_back 函数即可实现。
    2. 对于字符串与字符串的尾插,通过调用 append 函数进行实现。
//运算符重载 += 的实现:尾插入一个字符
string& string::operator+=(char c) {
    //复用 push_back 函数
    this->push_back(c);
    return *this;
}

//运算符重载 += 的实现:尾插入一个字符串
string& string::operator+=(const char* s) {
    //复用 append 函数
    this->append(s);
    return *this;
}

5.4 insert

insert 函数:

  • 函数原型:
    1. void insert(size_t pos, char c);
    2. void insert(size_t pos, const char* s);
  • 函数功能:
    1. 在 pos 位置插入一个字符。
    2. 在 pos 位置插入一个字符串。

代码实现 1:在 pos 位置插入一个字符

// insert 函数的实现:在 pos 位置插入一个字符
void string::insert(size_t pos, char c) {
    //保证 pos 位置
    assert(pos <= _size);
    //判断是否需要进行扩容操作
    if (_size == _capacity) {
        size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
        reserve(newcapacity);
    }
    size_t end = _size + 1;
    while (end > pos) {
        _str[end] = _str[end - 1];
        --end;
    }
    _str[pos] = c;
    _size++;
}

代码注意事项: 化解 size_t 死循环危机:把 end 的初始值设为 _size + 1,并用 end > pos 作为条件。 保证了即使在 pos = 0(头部插入)的最极端情况下,end 减到 0 循环就会立刻停止,绝不会发生 0 - 1 变成巨大正数(整型下溢)导致的内存越界崩溃。

错误代码示例:

// 典型的错误写法(容易引发灾难)
size_t end = _size;
while (end >= pos) {
    _str[end + 1] = _str[end];
    --end;
}

错误原因分析:

  1. 假设你要在头部插入字符,也就是 pos = 0。
  2. 当 end 减到 0 时,把第 0 个字符往后挪完,接着执行 --end,因为 size_t 不能是负数,0 - 1 会直接发生整型下溢,变成一个极其巨大的正数(比如 4294967295)。
  3. 此时循环条件 end >= 0 依然成立!程序就会陷入死循环,并疯狂篡改非法内存,最终导致程序崩溃。

代码实现 2:在 pos 位置插入一个字符串

//insert 函数的实现:在 pos 位置插入一个字符串
void string::insert(size_t pos, const char* s) {
    assert(pos <= _size);
    //计算当前字符串的长度,判断是否需要进行扩容处理
    size_t len = strlen(s);
    //特殊处理,当 len 等于 0 的时候
    if (len == 0) return;
    if (len + _size > _capacity) {
        size_t newcapacity = len + _size > _capacity * 2 ? len + _size : _capacity * 2;
        reserve(newcapacity);
    }
    //进行移动字符保证字符串能够插入
    size_t end = _size + len;
    while (end > pos + len - 1) {
        _str[end] = _str[end - len];
        --end;
    }
    //插入字符串到指定 pos 的位置处
    for (size_t i = 0; i < len; i++) {
        _str[pos + i] = s[i];
    }
    _size += len;
}

这段代码的具体执行步骤是:

  1. 先搬家 \0:end 初始等于 _size + len。_str[end] = _str[_size],把原本在末尾的结束符 \0 挪到了新的队尾。
  2. 依次搬家后续字符:随着 --end,依次把原来队伍最后的字符,挪到加上 len 之后的新位置。
  3. 停止:循环条件是 end > pos + len - 1(等价于 end >= pos + len),当 end 等于 pos + len 时,刚好执行 _str[pos + len] = _str[pos];。
  4. 这意味着,原本在 pos 位置的那个字符,被挪到了它该去的新位置,搬家正式结束。 此时,从 _str[pos] 到 _str[pos + len - 1] 这段空间就被彻底腾空了,正好用来执行你代码最后的那段 for 循环:把新的字符串 s 塞进这个空位里。

5.5 erase

erase 函数:

  • 函数原型:void erase(size_t pos, size_t len = npos);
  • 函数功能:删除字符串从 pos 位置开始的 len 个字符,len 的默认长度为 size_t npos=-1 (其值为 4294967295)。
  • 一般分为两种情况:
    1. pos 位置及其之后的有效字符都需要被删除。
    2. pos 位置及其之后的有效字符只需删除一部分。

代码实现:

//erase 函数的实现:从 pos 位置删除 len 个字符
void string::erase(size_t pos, size_t len) {
    //判断 pos 位置是否合理
    assert(pos < _size);
    //判断删除的长度 len 的情况
    if (len >= _size - pos) {
        _str[pos] = '\0';
        _size = pos;
    } else {
        size_t begin = pos + len;
        while (begin <= size()) {
            _str[begin - len] = _str[begin];
            ++begin;
        }
        //更新 size 的个数
        _size -= len;
    }
}

5.6 substr

substr 函数:

  • 函数原型:string substr(size_t pos, size_t len)
  • 函数功能:从 pos 位置开始 (包括 pos),截取 len 个大小的字符。

代码实现:

string string::substr(size_t pos, size_t len) {
    assert(pos < _size);
    if (len >= _size - pos) {
        //更新一下当前的 len
        len = _size - pos;
    }
    string tmp; //预留空间,避免多次扩容
    tmp.reserve(len);
    for (size_t i = 0; i < len; i++) {
        tmp += _str[pos + i];
    }
    return tmp;
}

5.7 clear

clear 函数:

  • 函数原型:void clear();
  • 函数功能:用于将对象中存储的字符串置空。

代码实现:

//clear 函数的实现:清除字符串的内容
void string::clear() {
    _str[0] = '\0';
    _size = 0;
}

六、访问字符串相关函数

6.1 operator[]

operator 函数重载:

  • 函数原型:
    1. char& operator[] (size_t pos)
    2. const char& operator[] (size_t pos) const
  • 函数功能:[] 运算符的重载使 string 对象能够像 C 字符串一样,通过下标访问特定位置的字符。

代码实现:

//A. 普通'读写版'
char& operator[] (size_t pos) {
    //断言:保证下标位置在有效的范围区间内
    assert(pos < _size);
    return _str[pos];
}

//B. 常量'只读版'
const char& operator[] (size_t pos) const {
    //断言:保证下标位置在有效的范围区间内
    assert(pos < _size);
    return _str[pos];
}

6.2 find

find 函数:

  • 函数原型:
    1. size_t find(char c, size_t pos = 0);
    2. size_t find(const char* s, size_t pos = 0);
  • 函数功能:
    1. 从 pos 位置(默认为 0)查找一个字符,查找失败返回 npos,查找成功返回第一个字符的位置下标。
    2. 从 pos 位置(默认为 0)查找一个字符串,查找失败返回 npos,查找成功返回字符串中第一个字符的位置。

代码实现 1:从 pos 位置向后查找一个字符,返回查找到的字符下标

//find 函数的实现:从 pos 位置向后查找一个字符,返回查找到的字符下标
size_t string::find(char c, size_t pos) {
    assert(pos < _size);
    for (size_t i = pos; i < _size; i++) {
        if (_str[i] == c) {
            return i;
        }
    }
    return npos;
}

代码实现 2:从 pos 位置向后查找一个字符串,返回查找

//find 函数的实现:从 pos 位置向后查找一个字符串,返回查找
size_t string::find(const char* s, size_t pos) {
    assert(pos < _size);
    //for (size_t i = pos; i + s.size() <= _size; i++)
    //{
    //    size_t j = 0;
    //    while (j < s.size())
    //    {
    //        if (_str[pos + j] != _str[j])
    //        {
    //            break;
    //        }
    //        j++;
    //    }
    //    if (j == s.size()) return i;
    //}
    const char* ptr = strstr(_str, s); //在母串 str 中,能够查找到字串 s
    if (ptr != nullptr) {
        return ptr - _str;
    } else {
        return npos;
    }
}

七、比较运算符重载

C++ 提供了六个关系运算符:>、>=、<、<=、==和!=。

在实际应用中,我们只需为类重载其中两个运算符,其余四个可以通过已重载的运算符来实现。

例如:对于 string 类,我们可以选择只重载 < 和 == 这两个关系运算符。

//重载运算符<
bool operator<(const string& s1, const string& s2) {
    return strcmp(s1.c_str(), s2.c_str()) < 0;
}

//重载运算符==
bool operator==(const string& s1, const string& s2) {
    return strcmp(s1.c_str(), s2.c_str()) == 0;
}

剩余运算符:>、>=、<=和!=,通过复用上述关系运算符。

//重载运算符<=
bool operator<=(const string& s1, const string& s2) {
    return s1 < s2 || s1 == s2;
}

//重载运算符>
bool operator>(const string& s1, const string& s2) {
    return !(s1 <= s2);
}

//重载运算符>=
bool operator>=(const string& s1, const string& s2) {
    return !(s1 < s2);
}

//重载运算符!=
bool operator!=(const string& s1, const string& s2) {
    return !(s1 == s2);
}

八、字符串输入与输出

8.1 >>运算符的重载

operator>> 函数重载:

  • 函数原型:istream& operator>>(istream& in, string& s)
  • 函数功能:重载 >> 运算符旨在使 string 对象能够像内置类型一样直接使用输入操作。

代码实现:

//流提取运算符重载的实现:输入字符串
istream& operator>>(istream& in, string& s) {
    //清除原有的字符
    s.clear();
    //预留缓冲数组,避免扩容
    const int N = 256;
    char buff[N] = { 0 };
    int i = 0;
    //清除前导空白区域
    char ch = in.get();
    while (ch == ' ' || ch == '\n' || ch == '\t') {
        ch = in.get(); // 只要是空白字符,就继续往下读,丢弃掉
    }
    //遇到换行 和 空格停止
    while (ch != ' ' && ch != '\n' && ch != '\t') {
        buff[i++] = ch;
        if (i == N - 1) {
            buff[i] = '\0';
            s += buff;
            i = 0;
        }
        ch = in.get();
    }
    //预留数组未满,就提前结束
    if (i > 0) {
        buff[i] = '\0';
        s += buff;
    }
    return in;
}

代码详解:

  1. 重置状态 (s.clear()):确保 cin >> s 是覆盖写入,而不是追加。
  2. 准备'运货小推车' (buff[256]):通过局部数组攒字符,避免频繁调用 += 导致底层频繁的内存扩容。
  3. 精准打击(跳过前导空白):这是你新加的逻辑。无论用户在正式输入前敲了多少个空格或回车,全被这个 while 循环吞掉,直到碰见真正的有效字符。
  4. 高效搬运(核心循环):遇到非空白字符开始装车。车装满了(i == N - 1)就卸货到 s 里,然后继续装。遇到下一个空格或换行,就意味着'这个单词读完了',立即停车。
  5. 打扫战场(扫尾工作):如果最后车里还有剩的货(i > 0),一次性全卸给 s。

8.2 <<运算符的重载

operator<< 函数重载:

  • 函数原型:ostream& operator<<(ostream& out, const string& s);
  • 函数功能:重载<<运算符是为了让 string 对象能够像内置类型一样使用<<运算符直接输出打印。

代码实现:

//流插入运算符重载的实现:输出字符串
ostream& operator<<(ostream& out, const string& s) {
    for (auto ch : s) {
        out << ch;
    }
    return out;
}

目录

  1. 一、string 类总览
  2. 成员变量分析
  3. 迭代器分析
  4. 二、默认成员函数
  5. 2.1 无参构造函数
  6. 2.2 带参构造函数
  7. 2.3 拷贝构造函数
  8. 2.4 赋值运算符重载
  9. 2.5 析构函数
  10. 三、迭代器
  11. 3.1 begin
  12. 3.2 end
  13. 四、容量和大小相关函数
  14. 4.1 size
  15. 4.2 capacity
  16. 4.3 reserve
  17. 4.4 empty
  18. 五、字符串修改函数
  19. 5.1 push_back
  20. 5.2 append
  21. 5.3 operator+=
  22. 5.4 insert
  23. 5.5 erase
  24. 5.6 substr
  25. 5.7 clear
  26. 六、访问字符串相关函数
  27. 6.1 operator[]
  28. 6.2 find
  29. 七、比较运算符重载
  30. 八、字符串输入与输出
  31. 8.1 >>运算符的重载
  32. 8.2 <<运算符的重载
  • 💰 8折买阿里云服务器限时8折了解详情
  • GPT-5.5 超高智商模型1元抵1刀ChatGPT中转购买
  • 代充Chatgpt Plus/pro 帐号了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • VS Code 前端开发 AI 助手对比:Copilot、通义灵码、iFlyCode 与 Trae
  • 基于 Isaac Lab 的机器人行走训练教程
  • 农业大模型:关键技术、应用场景与未来趋势
  • 黑客技术零基础入门学习指南
  • MATLAB A*路径规划算法仿真与改进实现
  • 基于 FPGA 的北斗导航自适应抗干扰算法设计与实现
  • 自动化机器学习框架 NexusCore 1.0 稳定版核心功能与快速开始
  • Python 调用 SolidWorks API 实现自动化建模示例
  • Maven 执行 install 或 compile 报错处理
  • Ubuntu 下 llama.cpp 编译与性能调优实战
  • Lua 元表与元方法详解
  • 前端常用加密方式详解:Base64、MD5、AES 及 RSA
  • 大语言模型(LLM)核心原理与实战指南
  • Python 使用 PyPDF2 实现 PDF 加密与密码破解
  • 基于 Dify 与 Qwen3-VL 的低代码视觉智能应用构建
  • 基础算法技巧总结:数据结构与数论模板
  • 就业环境变化下的数据分析技能提升与职业路径规划
  • 基于 DeepFace 和 OpenCV 的情绪分析器实现
  • 宇树 Go2 机器人强化学习(RL)开发实操指南
  • 基于 Streamlit 构建 AutoGen AI Agent Web 应用

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online