当前位置: 首页 > news >正文

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; }
http://www.cnnetsun.cn/news/4285423.html

相关文章:

  • 拓扑排序与动态规划:从DAG路径计数到算法竞赛实战
  • PayloadsAllTheThings:54类Web漏洞的payload与绕过手法,一个仓库全收
  • 响应式接口传递业务意图
  • 发布流水线流量增长前要补哪些防线
  • Hoppscotch 实时通信测试:5分钟连上 WebSocket 与 SSE
  • OpenAI自研推理芯片Jalapeño:开发者如何通过API验证延迟与成本变化
  • Hermes Agent 技能系统完整教程:5 分钟从安装到做出第一个技能
  • Codex 5小时限制背后:AI编程智能体安装配置与工程实践指南
  • learn-claude-code 完整教程:17 节课从零搭建 Claude Code 同款编码智能体底座
  • 动销效果不明?一文理清一物一码系统方案思路,附靠谱服务商推荐
  • Hoppscotch 浏览器扩展安装与使用教程:打通本地 API 调试的完整指南
  • PayloadsAllTheThings 入门指南:如何把一份 Web 安全 Payload 资源库用到测试和防守两端
  • 5 分钟写出第一个 Godot 着色器:呼吸灯与扫描线实战
  • React 富文本编辑器选型:4 个真实场景,每个只给一个结论
  • PowerToys实用指南:窗口布局、快速启动与文件预览的日常痛点怎么解
  • 情感陪伴产品如何梳理价值主张
  • C++模板编程:从函数模板到类模板的工业级泛型实践
  • LiteParse 指定页码解析:target-pages 精准提取的 5 个实用技巧
  • 5G云通信+卫星IoT融合:架构逻辑、场景落地与工程实践
  • aigc检测太高怎么办?维普AI率和论文重复率怎样一起降
  • 用大语言模型处理非编码工作:从会议纪要到批量周报的实战指南
  • Windows系统文件Windows.Internal.Graphics.Display.DisplayEnhancementManagement.dll丢失找不到问题解决
  • Day 44:深入理解事件系统和瀑布 — 插件间通信的核心
  • 多模型接入与故障转移:摆脱OpenAI和Anthropic单点依赖的工程方案
  • 基于FreeRTOS的STM32电子秤系统设计:从传感器采集到数据存储的实战解析
  • Deep-Live-Cam 实时换脸工具:3 次点击换掉摄像头里的脸,免费开源完整教程
  • 猿辅导2023校招技术岗笔试(二)全解析:题型、算法与备考策略
  • 专利撰写Skill:用AI Agent将论文idea自动转化为专利交底书
  • DeepSeek 7B 微调把 RTX 4060 撑爆,我在深度学习入门里翻出这 4 个显存优化才跑通
  • DriveTeach-VLA:图像轨迹如何破解自动驾驶预训练难题