news 2026/8/29 11:07:09

C++学习笔记(一)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++学习笔记(一)

6. C++ 的第一个程序

C++ 兼容 C 语言绝大多数语法,因此 C 语言风格的hello world程序在 C++ 中仍可运行,但 C++ 也有专属的输入输出实现,核心差异体现在头文件与输入输出对象的使用上。

6.1 两种实现方式对比

实现风格代码示例关键说明
C 语言兼容版```cpp
#include<stdio.h>
int main()
{
printf("hello world\n");
return 0;
}
```1. 依赖 C 语言标准输入输出头文件<stdio.h>;2. 编译要求:文件后缀需改为.cpp(VS 编译器会调用 C++ 编译器,Linux 环境需用g++编译,而非 C 语言的gcc);3. 本质是复用 C 语言的printf函数实现输出。
C++ 专属版```cpp
#include<iostream>
using namespace std;
int main()
{
cout << "hello world\n" << endl;
return 0;
}
```1. 依赖 C++ 标准输入输出流库头文件<iostream>;2.std是 C++ 标准库命名空间,using namespace std用于简化代码,避免每次使用标准库组件都添加std::前缀;3.cout是 C++ 标准输出对象,<<为流插入运算符,endl的作用是插入换行符并刷新缓冲区。

7. 命名空间

命名空间(namespace)是 C++ 为解决 “命名冲突” 问题设计的核心机制,通过将变量、函数、类等标识符封装在独立的作用域中,避免全局作用域内的名字污染。

7.1 命名空间的价值

在 C 语言中,全局作用域下的变量、函数极易发生命名冲突(例如自定义的rand变量与标准库中的rand函数冲突)。C++ 引入命名空间后,通过 “域隔离” 解决此问题:

  • 不同命名空间内可定义同名标识符,编译时编译器会按 “作用域” 查找标识符的声明或定义,有效避免冲突;
  • C++ 标准库(如coutvector等)全部封装在std(standard)命名空间中。

7.2 命名空间的定义规则

  1. 基本定义:使用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; }; }
  2. 嵌套定义:命名空间支持嵌套,可用于解决多层级的命名冲突问题。

    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; } } }
  3. 多文件合并特性:不同文件中定义的同名命名空间,编译器会自动将其合并为一个。例如在Stack.hQueue.h中分别定义的namespace bit,最终会被视为同一个命名空间。
  4. 作用域特性
    • 命名空间仅影响编译器对标识符的 “查找逻辑”,不影响变量的生命周期;
    • C++ 中的作用域类型包括:函数局部域、全局域、命名空间域、类域。

7.3 命名空间的使用方式

编译器默认仅在局部域和全局域中查找标识符,若需使用命名空间内的成员,需通过以下方式访问,推荐度从高到低依次为:

  1. 指定命名空间访问(项目开发推荐):通过命名空间名::成员名的格式精准访问,无命名冲突风险。

    cpp

    运行

    int main() { // 访问bit命名空间中的rand变量 printf("%d\n", bit::rand); // 访问bit::pg子命名空间中的Add函数 printf("%d\n", bit::pg::Add(1, 2)); return 0; }
  2. 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; }
  3. 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;(自动识别ab的类型并读取)
std::cout标准输出对象,对应屏幕输出,<<为流插入运算符,用于输出数据cout << a << " " << b << endl;
std::endl特殊操作符,作用是插入换行符并刷新输出缓冲区(区别于\n仅插入换行符)cout << "hello C++" << endl;
<iostream>C++ 标准输入输出流库头文件,必须包含才能使用cincoutendl代码开头需添加#include <iostream>

8.2 关键特性与注意事项

  1. 自动类型识别:编译器通过函数重载实现变量类型的自动匹配,无需像printf/scanf那样指定格式符(如%d%lf)。

    cpp

    运行

    int a = 0; double b = 0.1; char c = 'x'; // 自动识别类型并输出 cout << a << " " << b << " " << c << endl; // 自动识别类型并读取 cin >> a >> b >> c;
  2. 效率优化:在大量输入的场景(如算法竞赛)中,可通过以下代码关闭 C 与 C++ IO 流的同步,提升输入输出效率:

    cpp

    运行

    #include<iostream> using namespace std; int main() { ios_base::sync_with_stdio(false); // 关闭C和C++ IO同步 cin.tie(nullptr); // 解除cin与cout的绑定 cout.tie(nullptr); // 后续输入输出操作效率提升 return 0; }
  3. 命名空间依赖cincoutendl均属于 C++ 标准库,封装在std命名空间中,使用时需通过std::前缀或using namespace std展开。

9. 缺省参数

缺省参数(又称默认参数)是指在声明或定义函数时,为函数的形参指定一个默认值。调用函数时,若未传递实参,则使用形参的默认值;若传递了实参,则使用指定的实参。

9.1 缺省参数的分类与规则

分类代码示例核心规则
全缺省参数```cpp
void 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)均合法)。
半缺省参数```cpp
void Func2(int a, int b = 10, int c = 20)
{
cout << "a = " << a << ", b = " << b << ", c = " << c << endl;
}
```仅部分形参指定默认值,必须从右往左连续缺省,不可间隔缺省(如void Func(int a=10, int b)为非法写法);调用时需从左到右传递实参(如Func2(100)Func2(100,200)Func2(100,200,300)均合法)。

9.2 注意事项

  1. 声明与定义分离时,缺省值仅能在声明中指定:若函数声明和定义分离(如头文件.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; }
  2. 实用场景:初始化容器时,可根据需求指定初始大小。例如栈初始化时,默认开辟 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 不构成重载的情况

  1. 返回值不同不能作为重载条件:函数调用时,编译器无法根据返回值类型区分同名函数(如int f()void f(),调用f()会报错)。
  2. 缺省参数可能导致歧义:若同名函数的参数个数相同且包含缺省参数,调用时可能出现匹配歧义。例如void f1()void f1(int a=10),调用f1()时,编译器无法确定使用哪个函数,会报歧义错误。

11. 引用

引用是 C++ 中的重要概念,本质是给已存在的变量取一个 “别名”,引用与原变量共用同一块内存空间,编译器不会为引用开辟额外的内存。引用的核心用途是优化函数传参和返回值,减少数据拷贝。

11.1 引用的概念与定义

  • 语法格式:类型& 引用名 = 原变量(此处&为引用符号,非取地址符号,需与指针的&区分);
  • 代码示例:

    cpp

    运行

    #include<iostream> 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等同于修改a,a的值变为1 // 引用与原变量地址相同 cout << &a << endl; cout << &b << endl; cout << &c << endl; cout << &d << endl; return 0; }

11.2 引用的三大特性

  1. 定义时必须初始化:引用是变量的别名,必须在定义时明确绑定一个已存在的变量,未初始化的引用会导致编译报错(如int& ra;为非法写法)。
  2. 一个变量可以有多个引用:如同一个人可以有多个名字,一个变量也可以有多个别名(如上述代码中a的别名bcd)。
  3. 引用一旦绑定实体,不可更改指向:引用绑定某个变量后,始终指向该变量,无法重新指向其他变量。例如b = c的操作是将c的值赋给b(即赋给a),而非让b重新指向c

11.3 引用的实际用途

引用的核心价值在于 “减少数据拷贝” 和 “直接操作原变量”,主要应用于函数传参和函数返回值场景。

11.3.1 引用传参

引用传参可替代指针传参,简化代码逻辑,同时避免指针的复杂性(如二级指针),且能直接修改原变量的值,功能与指针传参一致。

  • 代码示例:实现交换函数

    cpp

    运行

    #include<iostream> 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

    运行

    #include<iostream> #include<assert.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; }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/29 11:06:07

拓扑排序与动态规划:从DAG路径计数到算法竞赛实战

1. 项目概述&#xff1a;从食物链到拓扑排序 最近在洛谷上刷题&#xff0c;又碰到了P4017这道经典题目——“最大食物链计数”。这题可以说是图论入门&#xff0c;特别是拓扑排序应用的一个绝佳练手案例。题目背景很有意思&#xff0c;它模拟了一个生态系统中的捕食关系&#x…

作者头像 李华
网站建设 2026/8/29 11:05:55

响应式接口传递业务意图

响应式接口传递业务意图“响应式接口传递业务意图”不是一张泛泛的检查表。它要回答的是&#xff1a;当前系统面对什么输入&#xff0c;允许消耗多少资源&#xff0c;失败时停在哪里&#xff0c;又由谁处理。页面渲染与用户交互往往跨过多个组件&#xff0c;问题也常藏在交界处…

作者头像 李华
网站建设 2026/8/29 11:05:41

发布流水线流量增长前要补哪些防线

发布流水线流量增长前要补哪些防线 发布系统在流量增长时&#xff0c;承受的不只是构建次数增加。代码仓库、构建运行器、制品库、镜像仓库、部署控制器和集群 API 都可能成为瓶颈。没有容量边界&#xff0c;某次高峰提交或事故期间的紧急修复&#xff0c;会让本该帮助恢复的流…

作者头像 李华
网站建设 2026/8/29 11:04:57

Hoppscotch 实时通信测试:5分钟连上 WebSocket 与 SSE

Hoppscotch 实时通信测试&#xff1a;5分钟连上 WebSocket 与 SSE 【免费下载链接】hoppscotch Open-Source API Development Ecosystem • https://hoppscotch.io • Offline, On-Prem & Cloud • Web, Desktop & CLI • Open-Source Alternative to Postman, Insomni…

作者头像 李华
网站建设 2026/8/29 11:04:29

OpenAI自研推理芯片Jalapeño:开发者如何通过API验证延迟与成本变化

前一段时间 AI 圈都在讨论大模型训练和推理的算力瓶颈&#xff0c;OpenAI 自研推理芯片 Jalapeo 的首秀消息正式落地。公开信息显示&#xff0c;OpenAI 用了约 9 个月时间完成这颗 3nm 制程推理芯片&#xff0c;主打能效与延迟双优。虽然这颗芯片不会像开源模型一样直接下载到本…

作者头像 李华