news 2026/10/22 13:14:29

C++ 入门笔记(十二):string 底层初探 —— basic_string 模板、构造与迭代器本质

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++ 入门笔记(十二):string 底层初探 —— basic_string 模板、构造与迭代器本质

C++ 入门笔记(十二):string 底层初探 —— basic_string 模板、构造与迭代器本质

第十二节课开始碰底层了:string 为什么是类模板、比较运算符为什么要写三组重载、手写 string 的构造怎么实现的、迭代器为什么本质就是指针。最让我醍醐灌顶的一点是 ——手写 string 的迭代器,就是char*指针本身,begin 返回首地址、end 返回 _str + _size,左闭右开。

开篇

学习方式不变:听课 + 疑惑的地方自己敲代码验证 + AI 辅助。这节代码全部在 VS(MSVC)实际编译运行:basic_string 四种特化、手写简化版 string(构造 + 迭代器)、比较运算符全局重载、迭代器接口统一性、常量区 vs 堆。


一、basic_string 为什么是类模板?

**

运行结果:

**

template <class charT, class traits = char_traits<charT>,

class Alloc = allocator<charT>>

class basic_string;

我们平时用的 string 其实是 basic_string<char> 的别名。它还有其他特化:

类型

对应

用途

string

basic_string<char>

普通 UTF-8/ASCII

wstring

basic_string<wchar_t>

旧 Windows 宽字符

u16string

basic_string<char16_t>

C++11,UTF-16

u32string

basic_string<char32_t>

C++11,UTF-32

实测:s1.size() = 5、ws1.size() = 5、u16.size() = 5、u32.size() = 5—— 长度都是 5,和编码无关。

如果直接写一个 string 类,那 wstring、u16string、u32string 就要分别写一份一模一样的代码,重复设计。模板把 "字符类型" 参数化,一份代码生成所有版本。wstring 现在用得少了(都用 u16/u32),但保留它是为了兼容旧 Windows 代码。


二、比较运算符为什么要写三组重载?

**

运行结果:

**

标准库 == 有三组重载:

bool operator==(const string& lhs, const string& rhs);

bool operator==(const char* lhs, const string& rhs); // char* 在左边

bool operator==(const string& lhs, const char* rhs); // char* 在右边

为什么不能写成成员函数?

成员函数版只能写成:bool operator==(const string& rhs) const——左操作数定死就是 string(因为是成员函数,左边是 this)。

那 str == s1(str 是 const char*)就编译不过了,因为左边是内置类型 char*,没法调用成员函数。

所以比较运算符必须写成全局函数,才能同时覆盖三种情况:string vs string、char* vs string、string vs char*。

比较规则和 strcmp 一样:ASCII 码逐位比,不是长度比。"xxx" vs "xxx11",到第三位都是 x,一个到结尾了一个还有,所以 "xxx" < "xxx11",返回 0。

为什么 str = s1 不能写?

str 是 const char*,赋值运算符没有重载把 my_string 赋给 const char*—— 编译器找不到匹配的 operator=,直接报错。


三、手写简化版 string:构造 + 迭代器

**

运行结果:

**

3.1 构造函数

my_string(const char* str = "")

: _size(strlen(str)) {

_capacity = _size;

_str = new char[_size + 1]; // 多开 1 字节存 \0

strcpy(_str, str);

}

  • _size = strlen(str):有效字符数,不含 \0
  • new char[_size + 1]:多开 1 字节,因为 strcpy 会把 '\0' 也拷过去
  • strcpy(_str, str):把常量区的字符串拷到堆上

3.2 迭代器就是 char*

typedef char* iterator;

iterator begin() { return _str; } // 首地址

iterator end() { return _str + _size; } // 左闭右开:有效长度末尾

实测:

  • for (auto ch : s) 和 while (it != s.end()) 输出完全一样 ——范围 for 本质就是迭代器遍历,编译器自动替换成 begin/end
  • 迭代器能直接当 char* 用:*p = 'H'、*(p+1) = 'E'、++it 全支持,输出 HEllo world

3.3 为什么 end 是 _str + _size 不是 _str + _capacity?

因为遍历的是有效字符,不是预留的容量。如果 end 指向 capacity 末尾,就会把后面空的内存也遍历出来,没有意义。

左闭右开 [begin, end) 和写 for 循环 for (i=0; i<len; i++) 完全呼应 —— 从 0 到 len-1 正好是有效元素。这种设计降低学习成本,知识能迁移。


四、迭代器包一层的意义:接口统一

**

运行结果:

**

实测:string、vector、list 三种完全不同的底层(数组、动态数组、链表),遍历代码写法完全一样:

for (auto ch : s) cout << ch << " ";

for (auto x : v) cout << x << " ";

for (auto x : lt) cout << x << " ";

如果直接拿 char* 遍历 string,那换个 list(链表节点不是连续内存)就完全用不了。包一层迭代器,底层换成什么指针 / 节点都不重要,对外接口 *it、++it、it != end() 永远一样。这就是解耦、降低耦合、统一接口。

顺便说:MSVC 下 std::string::iterator 不是裸 char*(是包装类),是为了加调试检查;但接口用法和 char* 完全一样。手写版直接用 char*,更直观。


五、常量区 vs 堆:构造之后发生了什么

**

运行结果:

**

实测两块地址完全不同:

str 地址(常量区): 00007FF79914F3C0

s[0] 地址(堆): 000000B059AFFA78

  • str = "hello world" 存在常量区,只读,改 str[0] 会崩溃
  • string s(str) 在堆上new char[strlen+1],可改
  • 修改 s[0] = 'H' 只影响堆上那份,常量区那份 "hello world" 没动

为什么 new char[_size+1] 要 +1?strlen ("hello world")=11,但 strcpy 要把 '\0' 也拷过去,实际占 12 字节。少开 1 字节就越界了。


结尾

这节课从 "会用 string" 进到了 "看懂 string 大概怎么实现的":它是 basic_string、比较运算符要三组全局重载、构造是 new char [_size+1]+strcpy、迭代器就是指针。下节课应该继续手写 string 或者开始 vector 了,学完再写下一篇。

学习方式:听课 + 疑惑处自己敲代码验证 + AI 辅助查缺补漏。所有示例均在 VS(MSVC)实际编译运行。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/22 13:13:59

NMVM——用于短文本聚类的非参数多视图模型

一、论文信息 论文标题&#xff1a; A Non-parametric Multi-view Model for Short Text Clustering中文标题&#xff1a; 一种用于短文本聚类的非参数多视图模型发表会议&#xff1a; KDD 2026论文链接&#xff1a; A Non-parametric Multi-view Model for Short Text Cluste…

作者头像 李华
网站建设 2026/10/22 13:13:10

rhcsa课程复习题

1.基础文件和目录操作(1). 在用户主目录下创建一个名为test_dir的目录&#xff0c;并在该目录中创建一个名为test_file.txt的空文本文件。[rootlocalhost ~]# mkdir -p ~/test_dir [rootlocalhost ~]# touch ~/test_dir/test_file.txt(2). 将test_file.txt文件复制到/tmp目录下…

作者头像 李华
网站建设 2026/10/22 13:12:41

Linux端口与连接排查:ss、lsof、nc、telnet、curl

「端口被占用了」「服务起不来报地址已被使用」「防火墙开了但连不上」——端口问题几乎每个部署日都会遇到。排查端口&#xff0c;需要回答四个问题&#xff1a;谁在监听、端口通不通、谁占用了、连接状态如何。这一篇用 ss、lsof、nc、telnet、curl 五把工具&#xff0c;串成…

作者头像 李华
网站建设 2026/10/22 13:10:44

零设计基础,快速制作旅游攻略海报的 AI 工作记录

平时偶尔需要做一些简易宣传海报&#xff0c;但是没有系统学习过 PS&#xff0c;自己找素材、调色、换背景、排版非常耗时间。这次有需求&#xff0c;想要做一版长沙旅游攻略海报&#xff0c;尝试借助 AI 图像工具来落地&#xff0c;把整套操作流程记录下来&#xff0c;给有同样…

作者头像 李华
网站建设 2026/10/22 13:09:50

机器视觉中CNN与Transformer的原理及应用差异

「CNN与ViT的分工逻辑」 目录 01 Transformer 与 CNN 应用范围对比 02 CNN&#xff1a;把"局部"写进结构里 03 Transformer&#xff08; ViT&#xff09;&#xff1a;把图像当成一句话 04 二者原理对比 05 落地分工&#xff1a;谁在哪儿干活 0…

作者头像 李华