前言:鉴于在C转C++的过程中,遇到的种种困难,我认为,有必要把我在学习C++的过程中和这套课程之间有所缺失的部分补齐,让初学者也能够听得懂,跟上名校的节奏,故写出此博客
CS106L C++ 学习笔记:第 1、2 课时(课程入门 · 字符串与流)
本合集把前两课合并为一篇:第 1 课时介绍课程工具、课程目标、C 与 C++ 的主要区别与标准 C++ 代码;第 2 课时深入讲解字符串、流(Stream)、ostringstream / istringstream 以及流的异常处理与状态位。
第一部分 · 第 1 课时:课程入门与概览
一、课程工具
- 必须使用 QT Creator:以前从来没有听过这个软件。
二、编写代码的不同方式
- 31:19:C++ 编写代码的不同方式。
三、C 语言与 C++ 的主要区别
- 36:23:
- 核心区别:C 语言无法抽象出"对象"与"类"的概念,无法表示复杂的数据总体。
- 正如视频中所说,struct 是一个开放的公共列表,功能过于简单,无法起到更大的作用。
- 这种更好的数据结构叫做类(class),和结构体是同一类东西,但更好用。
- ⚠️ 它是否还有更大的作用,有待后续学习验证。
四、课程目标
五、标准 C++ 代码的不同之处

第二部分 · 第 2 课时:字符串与流(Stream)
一、核心概念速览
- C++ 字符串:真正的字符串类型 std::string(C 语言的 char* 只是字符指针,不是真字符串)。
- 流(stream):一条能自动转换内容的"管道",把字符串 / 数字 / 小数统一输入输出。
- << / >>:<< 把右边的数据"送进"左边的流对象(输出);>> 从流中"取出"数据存进变量(输入)。
二、字符串:C 与 C++ 的区别
- C 语言里"字符串"实际是 char*(字符指针)。
- C++ 引入真正的字符串类 std::string:
- 可以直接赋值。
- 自动扩容空间。
- 支持直接载入。
三、流(Stream)
3.1 流存在的目的与 << 的作用
- 08:37:流存在的目的,以及 << 的作用和方式。
- 整个流就是你所输出内容的总和,它提供了一个整体的接口。
- 流能自动转化(类型转化)input 中的内容,字符串可被写入控制台。
3.2 缓冲区与流
- 12:06:缓冲区与流。
- 如何创建一个字符串输出流,流能自动转化其中的内容。
四、与 C 语言相比的四大不同
std::cout
- 把字符串放进流中,再由重载的 << 输出到屏幕上。
- 这就是"重载"的意义;cout / endl 替代了 C 的 printf,且具有实际语义。
运载重算符(向左执行)
- 优先取第一位的逻辑,把右边的数据输入到左边。
- cout 与 endl 类似 C 中的函数:endl 会修改 cout 流的状态并执行一个动作(换行 + 刷新)。
- 可以把字符直接载入右边的变量,无需提前开辟空间。
字符串类 std::string
- 可直接赋值、自动扩容、支持直接载入。
ostringstream 的用法
- 详见下节。
五、深入解析 ostringstream
5.1 语句各部分含义
ostringstream oss("Ito‑En Green Tea");
| ostringstream | 类名,代表"输出字符串流"(流管道类型) |
| oss | 对象变量名,自己取的名字 |
| ("Ito‑En Green Tea") | 构造函数传参:对象创建的瞬间,把字符串送入内部缓冲区,作为初始内容 |
这一步是对象初始化(构造),即创建对象的同时给内部成员赋初始值。
5.2 为什么不需要 = 等号赋值
C++ 类对象有两种初始化写法,效果等价:
// 写法1:括号直接初始化(当前代码)
ostringstream oss("Ito‑En Green Tea");
// 写法2:等号拷贝初始化,效果一样
ostringstream oss = ostringstream("Ito‑En Green Tea");
- 括号 () 的作用:在创建对象的那一行,直接调用类的构造函数,把括号内的数据交给对象完成初始化,因此不需要写 =。
- ⚠️ 注意:括号只在定义对象这一行代表构造传参。对象创建完成后,不能再写 oss("xxx")。
- ❌ 错误写法:对象建好后,不能直接用 = 给 ostringstream 赋值字符串。
ostringstream oss;
oss = "Ito‑En Green Tea"; // 编译报错,不支持该赋值
对比基础类型:int a = 10; 是内置基础类型,用等号赋值;而类对象支持"构造函数括号传参"的初始化语法。
5.3 ostringstream 与 std::string 的区别
两行初始化写法长得很像,但底层完全不同:
std::string s("Ito‑En Green Tea");
ostringstream oss("Ito‑En Green Tea");
| 本质 | 纯粹的字符串存储容器,专门存放字符 | 输出流管道对象,主打格式化写入 |
| 追加内容 | += / .append() | 重载运算符 << |
| 数字处理 | 不能直接写入数字,需手动转换 | oss << 123 << 3.14; 自动把数字转文本 |
| 取出内容 | 直接使用变量本身 | 必须调用 .str(),导出一份 std::string 拷贝 |
| 流能力 | 不是流,不能用 << 写入 | 属于 ostream 输出流,支持 << 管道写入 |
通俗比喻
- std::string:罐子,专门装文字,往里加文字用 +=。
- ostringstream:带管道入口的罐子,可以通过管道 << 丢字符串、数字、小数;想读取全部内容,调用 .str() 拿出来。
使用场景选择
重要小坑
.str() 返回的是缓冲区的拷贝快照,不是引用。
ostringstream oss("aaa");
std::string s = oss.str();
oss << "bbb";
// s 的内容不会跟着改变,s 保存的是调用 .str() 那一刻的副本
六、C 语言等价对照
6.1 ostringstream oss("Ito‑En Green Tea");
char buf[256];
strcpy(buf, "Ito‑En Green Tea");
6.2 oss << "16.9 Ounces";
strcat(buf, "16.9 Ounces");
- ostringstream 会自动扩容;C 语言 char 数组大小固定,容易缓冲区溢出。
- ostringstream 可以直接 <<数字;C 语言需要借助 sprintf 格式化。
七、<< 与 >> 的区别(直观体现)

八、深入解析 istringstream(输入流)

8.1 作用
- istringstream 输入流从已有字符串中读取数据:把变量命名后输入进去存储,再通过 >> 把数据输出成其他变量。
- 如图所示:
8.2 执行原理
以原始流内容 16.9 \\n Ounces. . \\t \\n \\n -38271 为例,流内部维护一个读取光标,每次读取后光标移动,逐个取出不同类型的数据:

8.3 核心规则
- 遇到空白字符(如 \\n、\\t)。
- 遇到对目标类型无效的字符。
8.4 自动转化形式

显然在这里我们能清晰地看见:当在内存中开辟了空间之后,任何流的输入输出都会自动转化形式。
九、istringstream 与 std::string 的区别
- s 是一个变量储存的容器,直接存放内容。
- iss 是 istringstream 功能的一个载体/替代品:你必须先"把东西塞进去",之后才能使用 istringstream 的读取功能。
- 需要分辨清楚 iss 与 s 的本质差别。
- 实际上我们要明白的是iss和istringstream之间是有很强的关联性的,实际上iss就代表着为了istringstream这个输出流开辟出的空间,s是存储内容是变量这一点不会有错,但是要将东西放入一个区域才能实现不同于C语言中繁琐的强制转化以及开辟空间的过程,直接使用istringstream就能实现流的直接传输

十、状态位(Stream State)
10.1 什么是状态位
- 当流解析失败或遇到错误时,会设置对应的状态位。
- 状态位用于标记流当前的健康状态:正常 / 文件末尾(EOF)/ 读取失败 / 写入失败等。
- 可以把状态位理解为流给程序的一份**“健康报告”**。

10.2 EOF 与错误信息
- EOF(End Of File,文件末尾) 实际上可以报出错误信息的具体情况,可以参考之前学过的 C 语言部分。
- ⚠️ 这一部分还需要多复习一点。


10.3 提取失败的提示
- 此处增加了一个"是否成功读取出整数"的提示:
- 如果没有在开头提取出来,就会报错;
- 然后进入下一个进程,判断后续有没有整数。

十一、异常处理:throw std::domain_error("xxx")
throw std::domain_error("xxx");
各部分含义:
行为:
- 合法输入:不 throw,return 结果。
- 非法输入:throw 抛出错误,代码停止,错误向上传递。
⚠️ 当前代码没有 try‑catch 捕获异常,一旦 throw,程序直接崩溃退出。


