C++ 首批课程:值语义、RAII与库存汇总项目 · 环境与完整起步至容器、算法与异常接口
本轮仅全文静态复核,示例未执行;下文Windows/Linux运行记录属于此前批次,不证明本轮修订源码已经运行。
这条路径从C++自身开始,不把C课程设为强制前置。先用string、vector和资源对象表达业务,再按需要深入底层;理解值/引用、所有权和资源生存期比记住一组手动new/delete写法更早。前置仅为会保存文件、切换终端目录与运行命令。
C++ 首批课程:值语义、RAII与库存汇总项目
本轮仅全文静态复核,示例未执行;下文Windows/Linux运行记录属于此前批次,不证明本轮修订源码已经运行。
这条路径从C++自身开始,不把C课程设为强制前置。先用string、vector和资源对象表达业务,再按需要深入底层;理解值/引用、所有权和资源生存期比记住一组手动new/delete写法更早。前置仅为会保存文件、切换终端目录与运行命令。
环境与完整起步
本批语言C++20、标准库;工具Zig0.15.2内置Clang及其配套C++库,分别验证Windows amd64 PowerShell和WSL Linux amd64 Bash。C++20是教学兼容设置,不是“最新C++”;不使用编译器扩展和第三方包。安装官方对应平台Zig,执行zig version;已有其他编译器须确认C++20和所需库支持。macOS不纳入本轮。
保存hello.cpp:
#include <cassert>
#include <iostream>
#include <string>
#include <vector>
int main()
{
std::vector<std::string> names{"C++", "RAII"};
auto copy = names;
copy.push_back("test");
const auto& borrowed = names;
assert(names.size() == 2 && copy.size() == 3 && borrowed.size() == 2);
for (const auto& name : names) std::cout << name << '\n';
}Windows执行zig c++ -std=c++20 -Wall -Wextra -Werror hello.cpp -o hello.exe再运行.\hello.exe;Linux输出hello并运行./hello。预期两行C++、RAII。不要使用zig cc把C++源码当C链接;语言前端、标准和标准库是一组工具链约定。
变量先初始化,auto推断静态类型,不是动态类型。函数按值接收时会创建参数值,按引用接收时使用已有对象;只读的较大对象常用const T&。有符号整数溢出不是可依赖的环绕;类型范围与容器长度用对应标准接口检查。
值、引用与容器生存期
hello里的copy拥有自己的vector/string元素;borrowed是原vector的别名,不复制它。引用必须绑定有效对象,不能返回局部对象的引用。vector会按需要扩容,导致原指针/引用/迭代器失效;不要取得元素引用后再push_back并继续使用旧引用。at可检查越界并抛异常,operator[]仍要求索引有效。
string拥有字符存储;string_view只是视图,不延长拥有者生存期。下面view.cpp可独立运行,owner在使用view期间一直存活且没有使它失效的修改:
#include <cassert>
#include <string>
#include <string_view>
int main()
{
std::string owner = "book";
std::string_view view = owner;
assert(view.substr(0, 2) == "bo");
std::string independent(view);
owner = "pen";
assert(independent == "book");
}owner赋新内容后不再读取view。标准字符串按字符单元存储字节序列,.size()不等于所有Unicode文字的视觉字符数。接口返回新结果时,通常返回拥有数据的string/vector;不为避免一次复制而返回悬空view。
RAII、移动与类的职责
RAII把资源释放与对象析构联系起来;ifstream、vector、unique_ptr都可管理各自资源。离开作用域或异常展开时析构负责清理,不用在每个return前复制一遍delete或close。GC不是C++的默认资源管理模型;对象可以在不同存储区域,其析构时机由生存期规则决定。
保存ownership.cpp:
#include <cassert>
#include <memory>
#include <utility>
int main()
{
auto owned = std::make_unique<int>(42);
auto moved = std::move(owned);
assert(!owned && *moved == 42);
}预期无输出、退出0。unique_ptr不能复制;移动转移其所有权,不能再解引用空的owned。std::move本身是转换表达式,实际是否移动由目标操作决定;一般移动后的对象处于有效但未指定状态,不能把unique_ptr保证为空的特性推广到所有类型。普通业务类优先组合已有资源对象,遵循Rule of Zero,避免无需求地同时编写五个特殊成员函数。
容器、算法与异常接口
vector用于顺序元素,map用于按键有序映射;unordered_map不承诺相同迭代顺序。算法配合迭代器与谓词工作;本项目用all_of验证名称,用from_chars严格解析整数。from_chars不隐式吞掉所有尾随字符,必须同时看错误码与结束指针。不要把stoi("12x")的前缀解析当成完整输入校验。
头文件声明接口,.cpp实现它,调用者在另一源文件中使用。模板定义通常需要在实例化处可见;本批不引入模块或模板库工程。函数的失败约定必须一致:库抛有意义异常,CLI入口统一记录并转换退出码。异常不是必然崩溃,捕获后也不能返回空结果伪装成功。
继续阅读
- 排错
- 开发实践
- 开发实践