ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

C++学习笔记(一)

C++学习笔记(一) 6. C 的第一个程序C 兼容 C 语言绝大多数语法因此 C 语言风格的hello world程序在 C 中仍可运行但 C 也有专属的输入输出实现核心差异体现在头文件与输入输出对象的使用上。6.1 两种实现方式对比实现风格代码示例关键说明C 语言兼容版cpp#includestdio.hint main(){printf(hello world\n);return 0;}1. 依赖 C 语言标准输入输出头文件stdio.h2. 编译要求文件后缀需改为.cppVS 编译器会调用 C 编译器Linux 环境需用g编译而非 C 语言的gcc3. 本质是复用 C 语言的printf函数实现输出。C 专属版cpp#includeiostreamusing namespace std;int main(){cout hello world\n endl;return 0;}1. 依赖 C 标准输入输出流库头文件iostream2.std是 C 标准库命名空间using namespace std用于简化代码避免每次使用标准库组件都添加std::前缀3.cout是 C 标准输出对象为流插入运算符endl的作用是插入换行符并刷新缓冲区。7. 命名空间命名空间namespace是 C 为解决 “命名冲突” 问题设计的核心机制通过将变量、函数、类等标识符封装在独立的作用域中避免全局作用域内的名字污染。7.1 命名空间的价值在 C 语言中全局作用域下的变量、函数极易发生命名冲突例如自定义的rand变量与标准库中的rand函数冲突。C 引入命名空间后通过 “域隔离” 解决此问题不同命名空间内可定义同名标识符编译时编译器会按 “作用域” 查找标识符的声明或定义有效避免冲突C 标准库如cout、vector等全部封装在stdstandard命名空间中。7.2 命名空间的定义规则基本定义使用namespace关键字 命名空间名 花括号{}花括号内可定义变量、函数、结构体等成员本质是创建一个独立的作用域。cpp运行// 以项目名“bit”作为命名空间名实际开发中可使用项目名或个人缩写 namespace bit { int rand 10; // 与标准库rand函数不冲突 int Add(int left, int right) { return left right; } // 命名空间内可定义结构体 struct Node { int val; struct Node* next; }; }嵌套定义命名空间支持嵌套可用于解决多层级的命名冲突问题。cpp运行namespace bit { // 子命名空间pg namespace pg { int rand 1; int Add(int left, int right) { return left right; } } // 子命名空间hg namespace hg { int rand 2; int Add(int left, int right) { return (left right) * 10; } } }多文件合并特性不同文件中定义的同名命名空间编译器会自动将其合并为一个。例如在Stack.h和Queue.h中分别定义的namespace bit最终会被视为同一个命名空间。作用域特性命名空间仅影响编译器对标识符的 “查找逻辑”不影响变量的生命周期C 中的作用域类型包括函数局部域、全局域、命名空间域、类域。7.3 命名空间的使用方式编译器默认仅在局部域和全局域中查找标识符若需使用命名空间内的成员需通过以下方式访问推荐度从高到低依次为指定命名空间访问项目开发推荐通过命名空间名::成员名的格式精准访问无命名冲突风险。cpp运行int main() { // 访问bit命名空间中的rand变量 printf(%d\n, bit::rand); // 访问bit::pg子命名空间中的Add函数 printf(%d\n, bit::pg::Add(1, 2)); return 0; }using展开单个成员使用using 命名空间名::成员名将命名空间中的单个成员纳入当前作用域适合高频使用且无冲突的成员。cpp运行// 展开bit::hg命名空间中的rand成员 using bit::hg::rand; int main() { // 直接使用展开的rand成员 printf(%d\n, rand); // 其他成员仍需指定命名空间 printf(%d\n, bit::pg::rand); return 0; }using展开全部成员仅推荐小练习使用使用using namespace 命名空间名将命名空间中的所有成员纳入当前作用域项目开发中易引发命名冲突不推荐使用。cpp运行// 展开bit命名空间的所有成员 using namespace bit; int main() { // 直接使用bit命名空间中的rand成员 printf(%d\n, rand); // 子命名空间成员仍需指定子命名空间 printf(%d\n, pg::Add(1, 2)); return 0; }8. C 输入 输出C 通过iostream库提供输入输出功能简称 IO 流核心优势是 “自动识别变量类型” 和 “支持自定义类型的输入输出”无需像 C 语言那样手动指定格式符。8.1 核心组件与功能组件功能说明用法示例std::cin标准输入对象对应键盘输入为流提取运算符用于读取数据int a; double b; cin a b;自动识别a和b的类型并读取std::cout标准输出对象对应屏幕输出为流插入运算符用于输出数据cout a b endl;std::endl特殊操作符作用是插入换行符并刷新输出缓冲区区别于\n仅插入换行符cout hello C endl;iostreamC 标准输入输出流库头文件必须包含才能使用cin、cout、endl代码开头需添加#include iostream8.2 关键特性与注意事项自动类型识别编译器通过函数重载实现变量类型的自动匹配无需像printf/scanf那样指定格式符如%d、%lf。cpp运行int a 0; double b 0.1; char c x; // 自动识别类型并输出 cout a b c endl; // 自动识别类型并读取 cin a b c;效率优化在大量输入的场景如算法竞赛中可通过以下代码关闭 C 与 C IO 流的同步提升输入输出效率cpp运行#includeiostream using namespace std; int main() { ios_base::sync_with_stdio(false); // 关闭C和C IO同步 cin.tie(nullptr); // 解除cin与cout的绑定 cout.tie(nullptr); // 后续输入输出操作效率提升 return 0; }命名空间依赖cin、cout、endl均属于 C 标准库封装在std命名空间中使用时需通过std::前缀或using namespace std展开。9. 缺省参数缺省参数又称默认参数是指在声明或定义函数时为函数的形参指定一个默认值。调用函数时若未传递实参则使用形参的默认值若传递了实参则使用指定的实参。9.1 缺省参数的分类与规则分类代码示例核心规则全缺省参数cppvoid Func1(int a 10, int b 20, int c 30){cout a a , b b , c c endl;}所有形参均指定默认值调用时可传递 0~n 个实参如Func1()、Func1(1)、Func1(1,2)、Func1(1,2,3)均合法。半缺省参数cppvoid Func2(int a, int b 10, int c 20){cout a a , b b , c c endl;}仅部分形参指定默认值必须从右往左连续缺省不可间隔缺省如void Func(int a10, int b)为非法写法调用时需从左到右传递实参如Func2(100)、Func2(100,200)、Func2(100,200,300)均合法。9.2 注意事项声明与定义分离时缺省值仅能在声明中指定若函数声明和定义分离如头文件.h中声明源文件.cpp中定义缺省参数只能在声明中设置定义中不可重复设置避免编译器产生歧义。cpp运行// Stack.h函数声明指定缺省值 typedef int STDataType; typedef struct Stack { STDataType* a; int top; int capacity; }ST; void STInit(ST* ps, int n 4); // Stack.cpp函数定义不指定缺省值 #include Stack.h void STInit(ST* ps, int n) { assert(ps n 0); ps-a (STDataType*)malloc(n * sizeof(STDataType)); ps-top 0; ps-capacity n; }实用场景初始化容器时可根据需求指定初始大小。例如栈初始化时默认开辟 4 个空间若已知需存储 1000 个数据可直接传递 1000 作为实参避免后续扩容操作。10. 函数重载函数重载是 C 的重要特性允许在同一作用域中定义同名函数但要求这些同名函数的形参满足 “不同”形参个数不同、形参类型不同或形参顺序不同从而实现灵活的函数调用。C 语言不支持函数重载。10.1 函数重载的判定条件重载类型代码示例说明形参类型不同cpp//int 类型参数的 Add 函数int Add(int left, int right){cout int Add(int, int) endl;return left right;}//double 类型参数的 Add 函数double Add(double left, double right){cout double Add(double, double) endl;return left right;}调用时编译器根据实参类型匹配对应的函数如Add(1,2)调用int类型版本Add(1.1,2.2)调用double类型版本。形参个数不同cpp// 无参版本的 f 函数void f(){cout f() endl;}// 一个 int 参数版本的 f 函数void f(int a){cout f(int) endl;}调用时编译器根据实参个数匹配对应的函数如f()调用无参版本f(10)调用int参数版本。形参顺序不同cpp// 先 int 后 char 的 f 函数void f(int a, char b){cout f(int, char) endl;}// 先 char 后 int 的 f 函数void f(char b, int a){cout f(char, int) endl;}调用时编译器根据实参顺序匹配对应的函数如f(10, a)调用第一个函数f(a, 10)调用第二个函数。10.2 不构成重载的情况返回值不同不能作为重载条件函数调用时编译器无法根据返回值类型区分同名函数如int f()和void f()调用f()会报错。缺省参数可能导致歧义若同名函数的参数个数相同且包含缺省参数调用时可能出现匹配歧义。例如void f1()和void f1(int a10)调用f1()时编译器无法确定使用哪个函数会报歧义错误。11. 引用引用是 C 中的重要概念本质是给已存在的变量取一个 “别名”引用与原变量共用同一块内存空间编译器不会为引用开辟额外的内存。引用的核心用途是优化函数传参和返回值减少数据拷贝。11.1 引用的概念与定义语法格式类型 引用名 原变量此处为引用符号非取地址符号需与指针的区分代码示例cpp运行#includeiostream using namespace std; int main() { int a 0; int b a; // b是a的别名 int c a; // c也是a的别名 int d b; // d是b的别名本质仍是a的别名 d; // 修改d等同于修改aa的值变为1 // 引用与原变量地址相同 cout a endl; cout b endl; cout c endl; cout d endl; return 0; }11.2 引用的三大特性定义时必须初始化引用是变量的别名必须在定义时明确绑定一个已存在的变量未初始化的引用会导致编译报错如int ra;为非法写法。一个变量可以有多个引用如同一个人可以有多个名字一个变量也可以有多个别名如上述代码中a的别名b、c、d。引用一旦绑定实体不可更改指向引用绑定某个变量后始终指向该变量无法重新指向其他变量。例如b c的操作是将c的值赋给b即赋给a而非让b重新指向c。11.3 引用的实际用途引用的核心价值在于 “减少数据拷贝” 和 “直接操作原变量”主要应用于函数传参和函数返回值场景。11.3.1 引用传参引用传参可替代指针传参简化代码逻辑同时避免指针的复杂性如二级指针且能直接修改原变量的值功能与指针传参一致。代码示例实现交换函数cpp运行#includeiostream using namespace std; // rx是x的别名ry是y的别名 void Swap(int rx, int ry) { int tmp rx; rx ry; ry tmp; } int main() { int x 0, y 1; cout 交换前x x , y y endl; Swap(x, y); // 直接传递变量无需传递地址 cout 交换后x x , y y endl; return 0; }实用场景在数据结构操作中如链表插入、栈初始化使用引用传参可避免二级指针。例如链表头插时使用ListNode* phead指针的引用直接修改头指针无需传递二级指针。11.3.2 引用返回值当函数返回的是 “函数结束后不会销毁的变量”如全局变量、类成员变量、动态内存分配的变量时使用引用返回可避免返回值的拷贝操作提升程序效率。若返回局部变量引用会成为 “野引用”局部变量销毁后引用指向的内存空间失效导致程序错误。代码示例获取栈顶元素栈顶元素存储在栈结构中函数结束后不销毁cpp运行#includeiostream #includeassert.h using namespace std; typedef int STDataType; typedef struct Stack { STDataType* a; int top; int capacity; }ST; void STInit(ST rs, int n 4) { rs.a (STDataType*)malloc(n * sizeof(STDataType)); rs.top 0; rs.capacity n; } void STPush(ST rs, STDataType x) { assert(rs.top rs.capacity); // 扩容逻辑此处省略 rs.a[rs.top] x; rs.top; } // 引用返回栈顶元素 int STTop(ST rs) { assert(rs.top 0); // 确保栈非空 return rs.a[rs.top - 1]; // 返回栈顶元素的引用 } int main() { ST st; STInit(st); STPush(st, 2); STTop(st) 10; // 直接修改栈顶元素2变为12 cout 栈顶元素 STTop(st) endl; // 输出12 return 0; }
返回列表