C++ 进阶知识点详细教程 - 第1部分
C++ 进阶知识点详细教程 - 第1部分
1. do while 循环
1.1 基本语法
do {
// 循环体
} while (条件);
关键特点:至少执行一次循环体,因为条件判断在循环体执行之后。
1.2 与while循环的区别
// while循环:先判断条件,再执行
int i = 10;
while (i < 5) {
cout << i << endl; // 不会执行,因为10 < 5为false
}
// do-while循环:先执行,再判断条件
int j = 10;
do {
cout << j << endl; // 会执行一次,输出10
} while (j < 5); // 然后判断10 < 5为false,退出循环
1.3 实际应用场景
场景1:菜单程序
#include <iostream>
using namespace std;
int main() {
int choice;
do {
cout << "======= 主菜单 =======" << endl;
cout << "1. 新建文件" << endl;
cout << "2. 打开文件" << endl;
cout << "3. 保存文件" << endl;
cout << "0. 退出程序" << endl;
cout << "请选择: ";
cin >> choice;
switch (choice) {
case 1: cout << "新建文件成功!" << endl; break;
case 2: cout << "打开文件成功!" << endl; break;
case 3: cout << "保存文件成功!" << endl; break;
case 0: cout << "感谢使用,再见!" << endl; break;
default: cout << "无效选择,请重新输入!" << endl;
}
cout << endl;
} while (choice != 0); // 只有选择0才退出
return 0;
}
场景2:输入验证
#include <iostream>
using namespace std;
int main() {
int score;
do {
cout << "请输入成绩(0-100): ";
cin >> score;
if (score < 0 || score > 100) {
cout << "输入无效!成绩必须在0-100之间。" << endl;
}
} while (score < 0 || score > 100);
cout << "你输入的成绩是: " << score << endl;
return 0;
}
场景3:游戏循环
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
int main() {
srand(time(0));
char playAgain;
do {
int secret = rand() % 100 + 1;
int guess, attempts = 0;
cout << "\n=== 猜数字游戏 ===" << endl;
cout << "我想了一个1-100之间的数字,你来猜!" << endl;
do {
cout << "请输入你的猜测: ";
cin >> guess;
attempts++;
if (guess < secret) {
cout << "太小了!再试试。" << endl;
} else if (guess > secret) {
cout << "太大了!再试试。" << endl;
} else {
cout << "恭喜你!猜对了!" << endl;
cout << "你总共猜了 " << attempts << " 次。" << endl;
}
} while (guess != secret);
cout << "再玩一次吗?(y/n): ";
cin >> playAgain;
} while (playAgain == 'y' || playAgain == 'Y');
cout << "游戏结束,谢谢游戏!" << endl;
return 0;
}
2. switch 语句
2.1 基本语法和规则
switch (表达式) {
case 常量1:
// 语句块1
break;
case 常量2:
// 语句块2
break;
case 常量3:
// 语句块3
break;
default:
// 默认语句块(可选)
break;
}
重要规则:
- 表达式类型:必须是整型(int、char、short、long)或枚举类型
- case常量:必须是编译时常量,不能是变量
- case唯一性:每个case后的常量值必须唯一,不能重复
- break的作用:跳出switch语句,防止继续执行下一个case
- default位置:可以放在任何位置,通常放在最后
2.2 break的重要性
没有break的后果(case穿透):
#include <iostream>
using namespace std;
int main() {
int day = 2;
cout << "没有break的情况:" << endl;
switch (day) {
case 1:
cout << "星期一" << endl;
case 2:
cout << "星期二" << endl; // 会执行这个
case 3:
cout << "星期三" << endl; // 也会执行这个!
case 4:
cout << "星期四" << endl; // 还会执行这个!
default:
cout << "其他" << endl; // 甚至会执行这个!
}
cout << "\n有break的情况:" << endl;
switch (day) {
case 1:
cout << "星期一" << endl;
break;
case 2:
cout << "星期二" << endl; // 只执行这个
break; // 然后跳出switch
case 3:
cout << "星期三" << endl;
break;
case 4:
cout << "星期四" << endl;
break;
default:
cout << "其他" << endl;
break;
}
return 0;
}
输出结果:
没有break的情况:
星期二
星期三
星期四
其他
有break的情况:
星期二
2.3 case常量的限制
#include <iostream>
using namespace std;
int main() {
int x = 10;
int choice = 1;
switch (choice) {
case 1: // 正确:字面常量
cout << "选择1" << endl;
break;
case 2 + 3: // 正确:常量表达式
cout << "选择5" << endl;
break;
// case x: // 错误!x是变量,不是常量
// cout << "选择x" << endl;
// break;
// case 1: // 错误!重复的case值
// cout << "重复" << endl;
// break;
default:
cout << "其他选择" << endl;
break;
}
return 0;
}
2.4 有意的case穿透(实用技巧)
#include <iostream>
using namespace std;
int main() {
int month;
cout << "请输入月份(1-12): ";
cin >> month;
switch (month) {
case 12: // 12月
case 1: // 1月
case 2: // 2月 - 这三个case都会执行到下面的语句
cout << "冬季 ❄️" << endl;
cout << "天气寒冷,注意保暖" << endl;
break;
case 3:
case 4:
case 5:
cout << "春季 🌸" << endl;
cout << "万物复苏,春暖花开" << endl;
break;
case 6:
case 7:
case 8:
cout << "夏季 ☀️" << endl;
cout << "天气炎热,注意防暑" << endl;
break;
case 9:
case 10:
case 11:
cout << "秋季 🍂" << endl;
cout << "秋高气爽,收获季节" << endl;
break;
default:
cout << "无效的月份!请输入1-12之间的数字。" << endl;
break;
}
return 0;
}
2.5 没有匹配case的情况
#include <iostream>
using namespace std;
int main() {
int score = 75;
char grade = score / 10; // 7
cout << "情况1:有default" << endl;
switch (grade) {
case 10:
case 9:
cout << "A等级" << endl;
break;
case 8:
cout << "B等级" << endl;
break;
case 7:
cout << "C等级" << endl; // 会执行这个
break;
default:
cout << "需要努力" << endl;
break;
}
grade = 5; // 改变值
cout << "\n情况2:没有default,且没有匹配的case" << endl;
switch (grade) {
case 10:
case 9:
cout << "A等级" << endl;
break;
case 8:
cout << "B等级" << endl;
break;
case 7:
cout << "C等级" << endl;
break;
// 没有default,且grade=5没有对应的case
// 结果:什么都不执行,直接跳过整个switch
}
cout << "switch执行完毕" << endl;
return 0;
}
2.6 switch vs if-else 对比
| 特性 | switch | if-else |
|---|---|---|
| 适用条件 | 等值比较 | 任意条件 |
| 性能 | 编译器可优化为跳转表 | 逐个判断 |
| 可读性 | 多分支时更清晰 | 复杂条件更灵活 |
| 支持类型 | 整型、字符型、枚举 | 任意布尔表达式 |
| 范围判断 | 不支持 | 支持 |
switch适用场景:
// 菜单选择、状态机、字符分类等
switch (menuChoice) {
case 1: createFile(); break;
case 2: openFile(); break;
case 3: saveFile(); break;
}
if-else适用场景:
// 范围判断、复杂条件
if (score >= 90) {
cout << "优秀";
} else if (score >= 80) {
cout << "良好";
} else if (score >= 60) {
cout << "及格";
} else {
cout << "不及格";
}
2.7 实战示例:计算器
#include <iostream>
using namespace std;
int main() {
double num1, num2, result;
char op;
cout << "简单计算器" << endl;
cout << "请输入表达式 (如: 5 + 3): ";
cin >> num1 >> op >> num2;
switch (op) {
case '+':
result = num1 + num2;
cout << num1 << " + " << num2 << " = " << result << endl;
break;
case '-':
result = num1 - num2;
cout << num1 << " - " << num2 << " = " << result << endl;
break;
case '*':
result = num1 * num2;
cout << num1 << " * " << num2 << " = " << result << endl;
break;
case '/':
if (num2 != 0) {
result = num1 / num2;
cout << num1 << " / " << num2 << " = " << result << endl;
} else {
cout << "错误:除数不能为0!" << endl;
}
break;
case '%':
if (num2 != 0) {
// 注意:%运算符只能用于整数
cout << (int)num1 << " % " << (int)num2 << " = "
<< (int)num1 % (int)num2 << endl;
} else {
cout << "错误:除数不能为0!" << endl;
}
break;
default:
cout << "错误:不支持的运算符 '" << op << "'" << endl;
cout << "支持的运算符:+ - * / %" << endl;
break;
}
return 0;
}
3. 流程图
3.1 基本符号
- 椭圆:开始/结束
- 矩形:处理
- 菱形:判断
- 平行四边形:输入/输出
3.2 示例
开始 → 输入n → 判断n>0? → 是:输出正数 / 否:输出负数 → 结束
4. string 字符串
4.1 基本操作
#include <string>
string s = "Hello";
s.length() // 长度
s.empty() // 判空
s += " World" // 连接
s[0] // 访问
4.2 常用函数
s.substr(0, 5) // 子串
s.find("World") // 查找
s.insert(5, ",") // 插入
s.erase(5, 6) // 删除
s.replace(6,5,"C++")// 替换
4.3 转换
to_string(123) // 数字→字符串
stoi("456") // 字符串→int
stod("3.14") // 字符串→double
5. 引用
5.1 什么是引用
引用是给已存在的变量起一个别名,引用和原变量共享同一块内存空间。
#include <iostream>
using namespace std;
int main() {
int a = 10;
int& ref = a; // ref是a的引用(别名)
cout << "a = " << a << endl; // 10
cout << "ref = " << ref << endl; // 10
cout << "&a = " << &a << endl; // 地址相同
cout << "&ref = " << &ref << endl; // 地址相同
ref = 20; // 修改ref就是修改a
cout << "修改ref后,a = " << a << endl; // 20
return 0;
}
5.2 引用的特点
- 必须初始化:声明引用时必须立即初始化
- 不能重新绑定:引用一旦绑定到某个变量就不能改变
- 没有独立内存:引用不占用额外的内存空间
- 不能为NULL:引用必须指向一个有效的对象
int a = 10, b = 20;
// 正确的引用声明
int& ref1 = a; // 必须初始化
// 错误的引用声明
// int& ref2; // 错误:引用必须初始化
// int& ref3 = NULL; // 错误:引用不能为NULL
// 引用不能重新绑定
ref1 = b; // 这不是让ref1指向b,而是把b的值赋给a
cout << a << endl; // 输出20,a的值被改变了
5.3 引用 vs 指针
| 特性 | 引用 | 指针 |
|---|---|---|
| 初始化 | 必须初始化 | 可以不初始化 |
| 重新指向 | 不能改变指向 | 可以改变指向 |
| 内存占用 | 不占用额外空间 | 占用指针大小的空间 |
| NULL值 | 不能为NULL | 可以为NULL |
| 运算 | 不能进行指针运算 | 可以进行指针运算 |
| 使用方式 | 直接使用 | 需要解引用(*) |
#include <iostream>
using namespace std;
int main() {
int a = 10, b = 20;
// 指针的使用
int* ptr = &a; // 指针指向a
cout << *ptr << endl; // 通过指针访问a的值:10
ptr = &b; // 指针可以改变指向
cout << *ptr << endl; // 现在指向b:20
// 引用的使用
int& ref = a; // 引用绑定到a
cout << ref << endl; // 直接访问:10
ref = b; // 这是赋值,不是改变引用指向
cout << a << endl; // a变成了20
cout << ref << endl; // ref仍然是a的别名:20
return 0;
}
5.4 函数参数:形参与实参
5.4.1 传值调用(值传递)
#include <iostream>
using namespace std;
// 形参:函数定义中的参数
void func1(int x) { // x是形参
x = 100; // 只修改形参的副本
cout << "函数内x = " << x << endl;
}
int main() {
int a = 10; // a是实参
cout << "调用前a = " << a << endl; // 10
func1(a); // 传递a的值给形参x
cout << "调用后a = " << a << endl; // 10(没有改变)
return 0;
}
输出结果:
调用前a = 10
函数内x = 100
调用后a = 10
5.4.2 引用传递
#include <iostream>
using namespace std;
// 形参是引用类型
void func2(int& x) { // x是实参的引用
x = 100; // 直接修改实参
cout << "函数内x = " << x << endl;
}
int main() {
int a = 10;
cout << "调用前a = " << a << endl; // 10
func2(a); // 传递a的引用给形参x
cout << "调用后a = " << a << endl; // 100(被修改了)
return 0;
}
输出结果:
调用前a = 10
函数内x = 100
调用后a = 100
5.4.3 指针传递
#include <iostream>
using namespace std;
// 形参是指针类型
void func3(int* x) { // x是指向int的指针
*x = 100; // 通过指针修改实参
cout << "函数内*x = " << *x << endl;
}
int main() {
int a = 10;
cout << "调用前a = " << a << endl; // 10
func3(&a); // 传递a的地址给形参x
cout << "调用后a = " << a << endl; // 100(被修改了)
return 0;
}
5.5 实用的引用应用
5.5.1 交换函数
#include <iostream>
using namespace std;
// 错误的交换函数(值传递)
void wrongSwap(int a, int b) {
int temp = a;
a = b;
b = temp;
// 只交换了形参的副本,实参不变
}
// 正确的交换函数(引用传递)
void correctSwap(int& a, int& b) {
int temp = a;
a = b;
b = temp;
// 直接交换实参
}
int main() {
int x = 10, y = 20;
cout << "交换前: x=" << x << ", y=" << y << endl;
wrongSwap(x, y);
cout << "错误交换后: x=" << x << ", y=" << y << endl;
correctSwap(x, y);
cout << "正确交换后: x=" << x << ", y=" << y << endl;
return 0;
}
5.5.2 避免大对象的复制
#include <iostream>
#include <vector>
#include <string>
using namespace std;
// 低效:传值会复制整个vector
void printVector1(vector<int> v) { // 复制开销大
cout << "Vector size: " << v.size() << endl;
for (int x : v) cout << x << " ";
cout << endl;
}
// 高效:传引用不复制
void printVector2(const vector<int>& v) { // const防止修改
cout << "Vector size: " << v.size() << endl;
for (int x : v) cout << x << " ";
cout << endl;
}
// 可以修改的引用传递
void addOne(vector<int>& v) {
for (int& x : v) { // 注意这里也用引用
x++;
}
}
int main() {
vector<int> nums = {1, 2, 3, 4, 5};
printVector2(nums); // 高效的只读访问
addOne(nums); // 修改vector
printVector2(nums); // 再次查看
return 0;
}
5.5.3 返回多个值
#include <iostream>
using namespace std;
// 通过引用参数返回多个值
void divideWithRemainder(int dividend, int divisor,
int& quotient, int& remainder) {
quotient = dividend / divisor;
remainder = dividend % divisor;
}
// 解二次方程 ax² + bx + c = 0
bool solveQuadratic(double a, double b, double c,
double& x1, double& x2) {
double discriminant = b * b - 4 * a * c;
if (discriminant < 0) {
return false; // 无实数解
}
x1 = (-b + sqrt(discriminant)) / (2 * a);
x2 = (-b - sqrt(discriminant)) / (2 * a);
return true;
}
int main() {
// 除法示例
int q, r;
divideWithRemainder(17, 5, q, r);
cout << "17 ÷ 5 = " << q << " 余 " << r << endl;
// 二次方程示例
double root1, root2;
if (solveQuadratic(1, -5, 6, root1, root2)) {
cout << "方程 x² - 5x + 6 = 0 的解:" << endl;
cout << "x1 = " << root1 << ", x2 = " << root2 << endl;
}
return 0;
}
5.6 常引用(const引用)
#include <iostream>
#include <string>
using namespace std;
int main() {
int a = 10;
const int& ref = a; // 常引用,不能通过ref修改a
cout << ref << endl; // 可以读取
// ref = 20; // 错误:不能修改
a = 20; // 可以直接修改a
cout << ref << endl; // ref会反映a的变化:20
// 常引用可以绑定到字面量
const int& ref2 = 100; // 正确
// int& ref3 = 100; // 错误:非常引用不能绑定字面量
return 0;
}
常引用的优势:
- 可以接受字面量和临时对象
- 防止意外修改
- 提高代码安全性
// 函数参数使用常引用
void printString(const string& s) { // 高效且安全
cout << s << endl;
// s += "!"; // 错误:不能修改
}
int main() {
string name = "Alice";
printString(name); // 传递变量
printString("Bob"); // 传递字面量
printString(name + "!"); // 传递临时对象
return 0;
}
6. 联合体(Union)
6.1 什么是联合体
联合体是一种特殊的数据类型,所有成员共享同一块内存空间。同一时刻只能存储其中一个成员的值。
#include <iostream>
using namespace std;
union Data {
int i; // 4字节
float f; // 4字节
char c; // 1字节
}; // 联合体大小 = 最大成员的大小 = 4字节
int main() {
Data d;
cout << "联合体大小: " << sizeof(d) << " 字节" << endl; // 4
cout << "int大小: " << sizeof(d.i) << " 字节" << endl; // 4
cout << "float大小: " << sizeof(d.f) << " 字节" << endl; // 4
cout << "char大小: " << sizeof(d.c) << " 字节" << endl; // 1
return 0;
}
6.2 联合体的内存共享
#include <iostream>
using namespace std;
union Data {
int i;
float f;
char c;
};
int main() {
Data d;
// 存储整数
d.i = 1000;
cout << "存储整数后:" << endl;
cout << "d.i = " << d.i << endl; // 1000
cout << "d.f = " << d.f << endl; // 垃圾值
cout << "d.c = " << (int)d.c << endl; // 垃圾值
// 存储浮点数(覆盖之前的整数)
d.f = 3.14f;
cout << "\n存储浮点数后:" << endl;
cout << "d.i = " << d.i << endl; // 垃圾值
cout << "d.f = " << d.f << endl; // 3.14
cout << "d.c = " << (int)d.c << endl; // 垃圾值
// 存储字符(覆盖之前的浮点数)
d.c = 'A';
cout << "\n存储字符后:" << endl;
cout << "d.i = " << d.i << endl; // 垃圾值
cout << "d.f = " << d.f << endl; // 垃圾值
cout << "d.c = " << d.c << endl; // A
return 0;
}
6.3 联合体 vs 结构体
#include <iostream>
using namespace std;
// 结构体:每个成员有独立的内存
struct StructData {
int i; // 偏移0,4字节
float f; // 偏移4,4字节
char c; // 偏移8,1字节
// 总大小:12字节(考虑内存对齐)
};
// 联合体:所有成员共享内存
union UnionData {
int i; // 偏移0,4字节
float f; // 偏移0,4字节
char c; // 偏移0,1字节
// 总大小:4字节(最大成员)
};
int main() {
StructData s;
UnionData u;
cout << "结构体大小: " << sizeof(s) << " 字节" << endl; // 12
cout << "联合体大小: " << sizeof(u) << " 字节" << endl; // 4
// 结构体:每个成员独立
s.i = 100;
s.f = 3.14f;
s.c = 'X';
cout << "\n结构体存储后:" << endl;
cout << "s.i = " << s.i << endl; // 100
cout << "s.f = " << s.f << endl; // 3.14
cout << "s.c = " << s.c << endl; // X
// 联合体:只能存储一个值
u.i = 100;
cout << "\n联合体存储整数后:" << endl;
cout << "u.i = " << u.i << endl; // 100
u.f = 3.14f; // 覆盖之前的整数
cout << "\n联合体存储浮点数后:" << endl;
cout << "u.f = " << u.f << endl; // 3.14
cout << "u.i = " << u.i << endl; // 垃圾值
return 0;
}
6.4 联合体的实际应用
6.4.1 节省内存空间
#include <iostream>
using namespace std;
// 一个变量在不同时刻需要不同类型的值
enum DataType { INT_TYPE, FLOAT_TYPE, CHAR_TYPE };
struct Variable {
DataType type; // 记录当前存储的类型
union {
int intVal;
float floatVal;
char charVal;
} value;
};
void printVariable(const Variable& var) {
switch (var.type) {
case INT_TYPE:
cout << "整数: " << var.value.intVal << endl;
break;
case FLOAT_TYPE:
cout << "浮点数: " << var.value.floatVal << endl;
break;
case CHAR_TYPE:
cout << "字符: " << var.value.charVal << endl;
break;
}
}
int main() {
Variable var;
// 存储整数
var.type = INT_TYPE;
var.value.intVal = 42;
printVariable(var);
// 存储浮点数
var.type = FLOAT_TYPE;
var.value.floatVal = 3.14f;
printVariable(var);
// 存储字符
var.type = CHAR_TYPE;
var.value.charVal = 'A';
printVariable(var);
return 0;
}
6.4.2 类型转换和位操作
#include <iostream>
#include <iomanip>
using namespace std;
// 查看浮点数的二进制表示
union FloatBits {
float f;
unsigned int bits;
};
// IP地址的不同表示
union IPAddress {
unsigned int addr; // 32位整数表示
unsigned char bytes[4]; // 4个字节表示
};
int main() {
// 浮点数位表示
FloatBits fb;
fb.f = 3.14f;
cout << "浮点数 " << fb.f << " 的二进制表示: "
<< hex << fb.bits << dec << endl;
// IP地址转换
IPAddress ip;
ip.bytes[0] = 192;
ip.bytes[1] = 168;
ip.bytes[2] = 1;
ip.bytes[3] = 100;
cout << "IP地址: "
<< (int)ip.bytes[0] << "."
<< (int)ip.bytes[1] << "."
<< (int)ip.bytes[2] << "."
<< (int)ip.bytes[3] << endl;
cout << "32位表示: " << ip.addr << endl;
return 0;
}
6.4.3 网络数据包解析
#include <iostream>
using namespace std;
// 网络数据包头部
union PacketHeader {
struct {
unsigned char version : 4; // 版本号(4位)
unsigned char headerLen : 4; // 头部长度(4位)
unsigned char typeOfService; // 服务类型(8位)
unsigned short totalLength; // 总长度(16位)
} fields;
unsigned int raw; // 原始32位数据
};
// 颜色的不同表示
union Color {
struct {
unsigned char r, g, b, a; // RGBA分量
} rgba;
unsigned int value; // 32位颜色值
};
int main() {
// 颜色示例
Color color;
color.rgba.r = 255; // 红色分量
color.rgba.g = 128; // 绿色分量
color.rgba.b = 0; // 蓝色分量
color.rgba.a = 255; // 透明度
cout << "颜色 RGB(" << (int)color.rgba.r << ", "
<< (int)color.rgba.g << ", " << (int)color.rgba.b << ")" << endl;
cout << "32位值: " << hex << color.value << dec << endl;
return 0;
}
6.5 匿名联合体
#include <iostream>
using namespace std;
struct Point {
int type; // 点的类型
union { // 匿名联合体
struct { int x, y; } point2D; // 2D坐标
struct { int x, y, z; } point3D; // 3D坐标
int coordinates[3]; // 数组形式
}; // 注意分号
};
int main() {
Point p;
p.type = 2; // 2D点
// 直接访问联合体成员,不需要联合体名称
p.point2D.x = 10;
p.point2D.y = 20;
cout << "2D点: (" << p.point2D.x << ", " << p.point2D.y << ")" << endl;
// 也可以通过数组访问
cout << "通过数组: (" << p.coordinates[0] << ", "
<< p.coordinates[1] << ")" << endl;
return 0;
}
6.6 联合体的注意事项
- 类型安全:程序员需要记住当前存储的是哪种类型
- 初始化:只能初始化第一个成员
- 构造函数:C++11后联合体可以有构造函数和析构函数
- 内存对齐:联合体的大小会考虑最严格的对齐要求
#include <iostream>
using namespace std;
union Data {
int i;
double d; // 8字节,需要8字节对齐
char c;
};
int main() {
Data d1 = {100}; // 只能初始化第一个成员
cout << "d1.i = " << d1.i << endl;
// Data d2 = {3.14}; // 错误:不能初始化第二个成员
cout << "联合体大小: " << sizeof(Data) << " 字节" << endl; // 8字节
return 0;
}
6.7 现代C++中的联合体
C++17引入了std::variant作为类型安全的联合体替代:
#include <iostream>
#include <variant>
#include <string>
using namespace std;
int main() {
// 类型安全的联合体
variant<int, float, string> v;
v = 42;
cout << "整数: " << get<int>(v) << endl;
v = 3.14f;
cout << "浮点数: " << get<float>(v) << endl;
v = string("Hello");
cout << "字符串: " << get<string>(v) << endl;
return 0;
}
联合体总结:
- 优点:节省内存,适合嵌入式系统
- 缺点:类型不安全,需要程序员管理
- 现代替代:
std::variant提供类型安全的选择
浙公网安备 33010602011771号