一、C++打印
#include <iostream>// 输入输出库(必须写)
using namespace std;// 简化代码
int main(int argc, const char * argv[]) {
// insert code here...
std::cout << "Hello, World!\n" << "Hello C++! 我正在用 XCode 学习~\n" ;
//简化后
cout << "Hello C++! 我正在用学习~" << endl;
return EXIT_SUCCESS;
}
二、程序的基本结构
1.以#开头的预处理编译指令
2.命名空间namespace
3.主函数main()与其参数
4.在Xcode中运行参数的命令行
#include <iostream>// 输入输出库(必须写)
//全局引用
//using namespace std;
int main(int argc, const char * argv[]) {
//局部引用
using std::cout;
using std::endl;
cout << argc << endl;
cout << argv[0] << endl;
cout << argv[1] << endl;
return EXIT_SUCCESS;
}
启动时传递的参数:Product -> Scheme ->Edit Scheme

cout << argc << endl;
cout << argv[0] << endl;
cout << argv[1] << endl;
打印结果
2 ------>代表一共有2个字符串
/Users/chenli/Library/Developer/Xcode/DerivedData/Build/Products/Debug/cppstudy ------>代表默认索引为可执行项目的全路径
134152344 ------>代表在Edit Scheme添加的参数
三、输入输出与变量常量
1.输入
//局部引用
using std::cout;
using std::endl;
using std::cin;//引用输入
int first,second;
cout << "请输入第一个数字:" << endl;
cin>>first;
cout << "请输入第二个数字:" << endl;
cin>>second;
cout << "和为:"<<first+second << endl;
2.变量与常量
int first,second; //变量 数值可修改
const int third=5; //常量 数值不可修改
常量一般全部大写用于区分
const int THIRD=5;
四、数据类型
1.整数类型
//有符号的整数类型
short int s_num=0;
long int l_num=0;
//无符号的整数类型
unsigned int u_num=0;
// 简写
unsigned u = 0;
unsigned short int us_num=0;
// 简写
unsigned short us = 0;
unsigned long int ul_num=0;
// 简写
unsigned long ul = 0;
// 无符号超长整型
unsigned long long int ull_num = 0;
// 简写
unsigned long long ull = 0;
2.字符类型
// 有符号字符(存字母、符号、数字字符)
char ch = 'A';
// 无符号字符
unsigned char uch = 'B';
3.布尔类型
// 真/假
bool flag = true;
bool is_ok = false;
4.浮点类型(小数)
// 单精度浮点数
float f_num = 3.14f;
// 双精度浮点数(最常用)
double d_num = 3.14159;
// 长双精度浮点数
long double ld_num = 3.1415926535;
5.字符串类型
#include <string> // 必须包含头文件
int main() {
// 正确写法
std::string str = "Hello World";
return 0;
}
//或
#include <string>
using namespace std; // 引入std命名空间
int main() {
// 可以直接使用string
string str = "Hello World";
return 0;
}
sizeof(变量名) 的使用:计算这个变量在内存中占用多少个字节(byte)
五、数组的声明、初始化、使用
1.数组声明语法
//类型 数组名[数组长度];
// 整型数组,能存 5 个 int
int arr[5];
// 浮点数组
float score[10];
// 字符数组
char name[20];
2、数组初始化
1. 声明时直接赋值
// 自动长度为 4
int arr[] = {10, 20, 30, 40};
// 指定长度 + 赋值
int arr[5] = {1, 2, 3, 4, 5};
// 部分赋值,剩下自动为 0
int arr[5] = {1, 2}; // {1,2,0,0,0}
2. 先声明,后赋值
int arr[3];
arr[0] = 11;
arr[1] = 22;
arr[2] = 33;
3.数组的使用
//数组名[下标]
int arr[4] = {10,20,30,40};
// 读取
cout << arr[0]; // 10
cout << arr[3]; // 40
// 修改
arr[1] = 99; // arr 变成 {10,99,30,40}
越界访问会报错 / 崩溃
4.遍历数组(循环输出)
int arr[5] = {1,2,3,4,5};
for(int i=0; i<5; i++){
cout << arr[i] << " ";
}
5.常用技巧:求数组长度
int arr[] = {10,20,30,40};
// 总字节数 / 单个元素字节数 = 元素个数
int len = sizeof(arr) / sizeof(arr[0]);
6.字符数组(字符串)
// 字符数组
char str[6] = {'h','e','l','l','o','\0'};
// 更常用
char str[] = "hello";
7.二维数组
// 2行3列
int arr[2][3] = {
{1,2,3},
{4,5,6}
};
// 访问
arr[0][1] = 99;
六、语句、表达式和运算符
1.表达式
//表达式 = 变量 + 常量 + 运算符
//特点:一定会算出一个值。
a + b
a = 10
a > 5
a++
2.语句
//语句 = 以分号;结尾的一行代码
//是程序执行的最小单位。
int a = 10; // 声明语句
a = a + 1; // 赋值语句
cout << a; // 输出语句
if(a > 0){} // 条件语句
for(;;){} // 循环语句
return 0; // 返回语句
3.表达式与语句的区分
- 有值的是表达式
- 能执行的是语句
4.运算符
1. 算术运算符
a + b // 加
a - b // 减
a * b // 乘
a / b // 除
a % b // 取余(模运算,整数专用)
a++ // 后置自增(先用再加)
++a // 前置自增(先加再用)
a-- // 后置自减
--a // 前置自减
2. 赋值运算符
a = 10; // 赋值
a += 5; // a = a + 5
a -= 5; // a = a - 5
a *= 5; // a = a * 5
a /= 5; // a = a / 5
a %= 5; // a = a % 5
3. 关系运算符(比较,结果是 true/false)
a == b // 等于
a != b // 不等于
a > b // 大于
a < b // 小于
a >= b // 大于等于
a <= b // 小于等于
4. 逻辑运算符
!a // 非(取反)
a && b // 与(两边都真才真)
a || b // 或(一边真就真)
5. 位运算符
a & b // 按位与
a | b // 按位或
a ^ b // 按位异或
~a // 按位取反
a << 1 // 左移
a >> 1 // 右移
6. 其他常用运算符
sizeof(a) // 占字节大小
&a // 取地址
*p // 解引用
?: // 三目运算符
三目运算符示例:
int max = (a > b) ? a : b;
5.运算符优先级(从高到低,记重点)
()括号++--(后置)!~*/%+-<><=>===!=&&||=+=-=等赋值
记不住就加括号,最稳。
6.常见语句分类
1.声明语句
int a;
int arr[5];
2.表达式语句
a = 1 + 2;
a++;
3.选择语句
if(){}
if(){}else{}
switch(){}
4.循环语句
while(){}
do{}while();
for(;;){}
5.跳转语句
break;
continue;
return;
goto;
特殊:goto的使用
是 C++ 里的无条件跳转语句,可以直接跳到代码里你标记的位置执行。
//先给某一行打一个标签(名字 + 冒号)
//用 goto 标签名; 跳过去
goto 标签名;
标签名:
要执行的代码;
例子1:
#include <iostream>
using namespace std;
int main() {
cout << "1" << endl;
// 直接跳到 FLAG 位置
goto FLAG;
cout << "2" << endl; // 这一行不会执行
FLAG:
cout << "3" << endl;
return 0;
}
输出结果:
1
3
//常用场景:出错时统一退出
int main() {
int a;
cin >> a;
if (a < 0) {
goto END; // 直接跳到结束
}
cout << "a = " << a << endl;
END:
cout << "程序结束" << endl;
return 0;
}
//用 goto 实现循环(不推荐,但能看懂)
int i = 0;
LOOP:
cout << i << endl;
i++;
if (i < 5) goto LOOP;
6.空语句
; // 只有一个分号
七、条件判断语句的使用
1.if 语句
1.if
if (条件) {
条件成立时执行这里;
}
//示例
int score = 90;
if (score >= 60) {
cout << "及格" << endl;
}
2.if … else
if (条件) {
成立执行这里;
} else {
不成立执行这里;
}
//示例:
if (score >= 60) {
cout << "及格";
} else {
cout << "不及格";
}
3. if … else if … else(多条件)
if (条件1) {
} else if (条件2) {
} else if (条件3) {
} else {
}
示例:
int score = 85;
if (score >= 90) {
cout << "优秀";
} else if (score >= 80) {
cout << "良好";
} else if (score >= 60) {
cout << "及格";
} else {
cout << "不及格";
}
4. 嵌套 if(if 里再套 if)
if (年龄 >= 18) {
if (成绩 >= 60) {
cout << "成年且及格";
} else {
cout << "成年但不及格";
}
} else {
cout << "未成年";
}
2.switch 语句
switch (变量) {
case 值1:
代码;
break;
case 值2:
代码;
break;
default:
都不满足时执行;
}
int week = 3;
switch (week) {
case 1:
cout << "周一";
break;
case 2:
cout << "周二";
break;
case 3:
cout << "周三";
break;
default:
cout << "未知";
}
八、循环语句的使用
1. while 循环(先判断,再执行)
while (条件) {
循环体代码;
}
int i = 1;
while (i <= 5) {
cout << i << endl;
i++; // 必须更新,否则死循环
}
2. do while 循环(先执行一次,再判断)
do {
循环体代码;
} while (条件);
int i = 1;
do {
cout << i << endl;
i++;
} while (i <= 5);
3. for 循环
for (初始化; 条件; 更新) {
循环体;
}
for (int i = 1; i <= 5; i++) {
cout << i << endl;
}
4. 循环控制语句
break
直接跳出整个循环,循环结束。
for (int i = 1; i <= 10; i++) {
if (i == 6) break;
cout << i << " "; // 输出 1 2 3 4 5
}
continue
跳过本次循环剩下的语句,直接进入下一次循环。
for (int i = 1; i <= 5; i++) {
if (i == 3) continue;
cout << i << " "; // 输出 1 2 4 5
}
5. 嵌套循环(循环里套循环)
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
cout << i << "," << j << " ";
}
cout << endl;
}
九、函数
1.函数的结构(四要素)
返回值类型 函数名(参数列表) {
函数体;
return 返回值;
}
- 返回值类型:函数执行完要返回什么类型的数据(int、double、void 等)
- 函数名:见名知意
- 参数列表:传给函数的数据
- 函数体:具体做什么
- return:把结果返回去(没有返回值用 void,可以不写 return)
2.最简单的函数
// 无返回值、无参数
void hello() {
cout << "Hello" << endl;
}
调用:
hello();
3.带参数的函数
// 求两个数的和
int add(int a, int b) {
return a + b;
}
调用:
int sum = add(3, 5); // sum = 8
4.带返回值 vs 无返回值
1. 有返回值(int、double、char…)
必须用 return 送回一个结果。
int max(int a, int b) {
return a > b ? a : b;
}
2. 无返回值(void)
不用 return,或者写 return;
void print(int x) {
cout << x << endl;
}
4.函数声明
如果函数写在 main 后面,必须在 main 前面声明,否则报错。
// 函数声明
int add(int a, int b);
int main() {
cout << add(2, 3);
}
// 函数定义
int add(int a, int b) {
return a + b;
}
5.函数重载
函数名相同,参数不同,C++ 自动匹配。
int add(int a, int b);
double add(double a, double b);
调用:
add(1, 2); // 调用 int 版本
add(1.1, 2.2); // 调用 double 版本
6.值传递
把变量的值复制一份传给函数,函数内部修改,外面的变量不会变。
void f(int x) {
x = 100;
}
int main() {
int a = 10;
f(a);
// a 还是 10
}
7.常用函数示例
1. 求最大值
int max(int a, int b) {
if (a > b) return a;
else return b;
}
2. 判断偶数
bool isEven(int n) {
return n % 2 == 0;
}
3. 打印数组
void printArr(int arr[], int len) {
for (int i = 0; i < len; i++) {
cout << arr[i] << " ";
}
}
十、指针和引用
1.指针
1.定义语法
指针就是存地址的变量。
- 普通变量:存数据
- 指针变量:存内存地址
类型 *指针名;
示例:
int a = 10;
int *p = &a; // p 指向 a 的地址
// &a:取地址符号,获取变量 a 的地址
// int *p:定义一个指向 int 类型的指针
2.解引用 *
通过指针拿到它指向的值:
int a = 10;
int *p = &a;
cout << *p; // 输出 10,*p 就是 a
3.用指针修改值
*p = 20; // 等价于 a = 20
4.指针和数组
数组名本质就是常量指针
int arr[3] = {1,2,3};
int *p = arr;
cout << *p; // 1
cout << *(p+1); // 2
cout << *(p+2); // 3
2.引用
1.定义语法
引用就是变量的别名,相当于给变量起外号。
类型 &别名 = 原名;
int a = 10;
int &b = a; // b 是 a 的引用
b = 20; // a 也变成 20
特点:
引用必须初始化
一旦绑定,不能再改绑别的变量
操作引用 = 操作原变量
3.指针 vs 引用(超级重点对比)
相同点
- 都能间接操作原变量
不同点
-
指针是变量,可空、可改指向
int *p = nullptr; // 可以为空
p = &a;
p = &b; // 可以改指向
2.引用是别名,不能为空,不能重绑
int &r; // 错误,必须初始化
r = c; // 不是改绑,是赋值
3.指针用 * 取值,引用直接用
4.指针可以做算术(p++),引用不行
4.最常用场景:函数传参
1. 指针做参数(地址传递)
void change(int *p) {
*p = 100;
}
int a = 10;
change(&a); // a 变成 100
2. 引用做参数(推荐)
void change(int &r) {
r = 100;
}
int a = 10;
change(a); // a 变成 100
好处:
- 比指针简单安全
- 不用写
* - 不会空指针
十一、类、对象和构造函数
1.类和对象
1. 类(class)
类就是自定义类型,把属性 + 行为打包在一起。
- 属性:成员变量
- 行为:成员函数
class 类名 {
public:
// 成员变量(属性)
// 成员函数(行为)
};
示例:
class Student {
public:
// 属性
string name;
int age;
// 行为
void show() {
cout << name << " " << age << endl;
}
};
2. 对象(Object)
对象就是类的具体实例。类是 “图纸”,对象是 “房子”。
//定义对象
Student s1;
//使用对象:
s1.name = "张三";
s1.age = 18;
s1.show();
3. 访问权限
public:公开,外部可以访问private:私有,只有类内部能访问(默认)protected:继承用
class A {
private:
int x; // 外部不能直接用
public:
void setX(int v) { x = v; }
};
2.构造函数
1. 什么是构造函数
- 创建对象时自动调用
- 用来初始化成员变量
- 名字必须和类名完全一样
- 没有返回值(连 void 都不用写)
2. 语法
类名() {
// 初始化
}
示例:
class Student {
public:
string name;
int age;
// 构造函数
Student() {
name = "无名";
age = 0;
}
};
创建对象时自动执行:
Student s;
// s.name 自动是 "无名"
3. 带参数的构造函数
Student(string n, int a) {
name = n;
age = a;
}
创建对象:
Student s1("张三", 18);
Student s2 = Student("李四", 19);
4. 默认构造函数
如果你一个构造函数都不写,编译器会自动给一个空的默认构造。但如果你写了任意构造函数,默认构造就消失。
3.析构函数
- 名字:
~类名() - 对象销毁时自动调用
- 无参、无返回值
- 用来释放资源(如指针)
~Student() {
cout << "对象销毁" << endl;
}
4.this 指针
每个成员函数里都有一个 this,代表当前对象自己。
void setName(string name) {
this->name = name;
}
// this-> 用来区分成员变量和同名参数。
5.完整示例
#include <iostream>
#include <string>
using namespace std;
class Student {
private:
string name;
int age;
public:
// 构造函数
Student(string n, int a) {
name = n;
age = a;
cout << "构造" << endl;
}
void show() {
cout << name << " " << age << endl;
}
~Student() {
cout << "析构" << endl;
}
};
int main() {
Student s("小明", 18);
s.show();
return 0;
}
十二、结构体与联合体
1.结构体(struct)
1. 是什么
把多个不同类型的变量打包在一起,变成一个新类型。
可以理解成:超级简单的类。
struct 结构体名 {
成员1;
成员2;
...
};
2. 定义结构体
struct Student {
int id;
string name;
int age;
};
3. 定义结构体变量(对象)
Student s;
4. 访问成员
用 . 访问:
s.id = 1001;
s.name = "张三";
s.age = 18;
5. 指针访问结构体(->)
Student *p = &s;
p->id = 1002;
p->name = "李四";
6. struct 和 class 的区别
class默认权限是privatestruct默认权限是public- 其他几乎一样(struct 也能写函数、构造函数)
2.联合体(union)
1. 是什么
多个成员共用同一块内存空间
同一时间,只能用其中一个成员
union 联合体名 {
成员1;
成员2;
...
};
示例:
union Data {
int i;
float f;
char c;
};
2.特点
- 所有成员从同一个地址开始
- 大小 = 最大成员的大小
- 改一个成员,其他成员的值会被覆盖
Data d;
d.i = 10; // 用 int
d.f = 3.14f; // 覆盖 int,现在只能用 float
3.大小计算
union Data {
int i; // 4
float f; // 4
char c; // 1
};
// sizeof(Data) = 4(取最大的 int/float)
3.结构体 vs 联合体对比
| 特性 | 结构体 struct | 联合体 union |
| 内存使用 | 各成员独立空间,依次排列 | 所有成员共用同一块内存 |
| 同时使用 | 可以同时访问所有成员 | 同一时间只能用一个成员 |
| 大小 | 所有成员大小之和(可能对齐) | 等于最大成员的大小 |
| 赋值影响 | 改一个不影响其他 | 改一个会覆盖其他所有成员 |
4.常用小例子
结构体示例
struct Point {
int x;
int y;
};
Point p = {10, 20};
cout << p.x << " " << p.y;
联合体示例(判断系统大小端)
union Test {
int a;
char b;
};
Test t;
t.a = 1;
if (t.b == 1)
cout << "小端";
else
cout << "大端";
十三、文件的读写
C++ 文件读写主要用文件流:
ifstream(读文件)、ofstream(写文件)、fstream(可读可写)。
1.头文件
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
2.写文件
1. 普通文本写入
// 创建写文件对象
ofstream ofs("test.txt");
// 判断是否打开成功
if (!ofs) {
cout << "打开失败" << endl;
return 0;
}
// 写入内容
ofs << "Hello 文件" << endl;
ofs << 123 << endl;
// 关闭文件
ofs.close();
2. 追加写入(不清空原有内容)
// 加 ios::app
ofstream ofs("test.txt", ios::app);
ofs << "追加一行" << endl;
ofs.close();
3.读文件(从文件读取)
方式 1:按行读取(最常用)
ifstream ifs("test.txt");
if (!ifs) {
cout << "打开失败" << endl;
return 0;
}
string line;
// 循环读每一行
while (getline(ifs, line)) {
cout << line << endl;
}
ifs.close();
方式 2:按空格 / 换行读取(类似 cin)
string s;
int a;
ifs >> s >> a; // 自动按空白分割
4.二进制读写
// 写
ofstream ofs("data.bin", ios::binary);
int x = 100;
ofs.write((char*)&x, sizeof(x));
// 读
ifstream ifs("data.bin", ios::binary);
int y;
ifs.read((char*)&y, sizeof(y));
5.文件打开模式(ios::)
ios::out // 写(默认)
ios::in // 读(默认)
ios::app // 追加
ios::binary // 二进制
ios::trunc // 清空原有内容
组合使用:
fstream fs("a.txt", ios::in | ios::out);
6.完整案例
//
// main.cpp
// cppstudy
//
// Created by 陈立 on 2026/4/21.
//
#include <fstream>
#include <iostream>
#include <string>
// 加上这个头文件
#include <filesystem>
using namespace std;
namespace fs = filesystem;
// 先声明函数(告诉编译器有这个函数)
void WriteFlie();
void AddWrite();
void ReadFile();
int main(int argc, const char * argv[]) {
WriteFlie();
AddWrite();
ReadFile();
return EXIT_SUCCESS;
}
void WriteFlie(){
// 创建写文件对象
ofstream ofs("test.txt");
// 判断是否打开成功
if (!ofs) {
cout << "打开失败" << endl;
return;
}
// 写入内容
ofs << "Hello 文件" << endl;
ofs << 123 << endl;
// 关闭文件
ofs.close();
// 直接输出完整路径
cout << "文件已写入:" << fs::absolute("test.txt") << endl;
}
void AddWrite(){
// 加 ios::app
ofstream ofs("test.txt", ios::app);
ofs << "追加一行" << endl;
ofs.close();
}
void ReadFile(){
ifstream ifs("test.txt");
if (!ifs) {
cout << "打开失败" << endl;
return ;
}
string line;
// 循环读每一行
while (getline(ifs, line)) {
cout << line << endl;
}
ifs.close();
}
输出写入文件目录:
// 加上这个头文件
#include <filesystem>
namespace fs = filesystem;
cout << "文件已写入:" << fs::absolute("test.txt") << endl;
十四、模版template
模板就是 **“代码的模具”,让一段代码通用多种数据类型 **,不用重复写 int、double、string 等版本。
1.函数模板
1. 语法
template <typename T> // 或 template <class T>
返回值 函数名(T 参数) {
// 使用 T 作为类型
}
template <> 声明这是模板
T 是类型参数(可以随便起名:T/Type/Elem)
typename 和 class 在这里完全一样
2. 示例:通用交换函数
template <typename T>
void swap(T &a, T &b) {
T temp = a;
a = b;
b = temp;
}
调用时自动适配类型:
int a = 1, b = 2;
swap(a, b);
double x = 1.1, y = 2.2;
swap(x, y);
string s1 = "hello", s2 = "world";
swap(s1, s2);
3. 多个类型参数
template <class T1, class T2>
void print(T1 a, T2 b) {
cout << a << " " << b << endl;
}
2.类模板
1. 语法
template <typename T>
class 类名 {
// 成员可以用 T
};
2. 示例:通用数组类
template <class T>
class MyArray {
private:
T arr[10];
public:
void set(int index, T val) {
arr[index] = val;
}
T get(int index) {
return arr[index];
}
};
使用时必须指定类型:
MyArray<int> arr1;
arr1.set(0, 10);
MyArray<double> arr2;
arr2.set(0, 3.14);
3.模板的特点
-
模板不是函数 / 类,是模具用到时才会根据类型生成具体代码
-
类型必须能支持模板里的操作比如模板里用了
+,传入的类型就必须支持加法 -
函数模板可以自动推导类型
add(1, 2); // 自动 T = int
-
类模板不能自动推导,必须写 <>
MyArray obj; // 错误
MyArray<int> obj; // 正确
4.最常用场景
- 通用算法:
swap、max、min、sort - 通用容器:
vector<T>、list<T>、map<T1,T2> - 减少重复代码,一套代码支持所有类型
示例 1:通用打印数组算法
#include <iostream>
using namespace std;
// 通用打印算法
template <typename T>
void printArray(T arr[], int len) {
for (int i = 0; i < len; i++) {
cout << arr[i] << " ";
}
cout << endl;
}
int main() {
// int数组
int a[] = {1,2,3,4,5};
printArray(a, 5);
// double数组
double b[] = {1.1, 2.2, 3.3};
printArray(b, 3);
// string数组
string s[] = {"apple", "banana", "orange"};
printArray(s, 3);
}
示例 2:通用求最大值算法
template <typename T>
T getMax(T a, T b) {
return a > b ? a : b;
}
// 使用
cout << getMax(10, 20) << endl;
cout << getMax(3.14, 2.71) << endl;
cout << getMax(string("zoo"), string("apple")) << endl;
示例3:通用栈容器(最经典)
#include <iostream>
using namespace std;
// 通用栈类模板
template <typename T, int SIZE = 10>
class MyStack {
private:
T arr[SIZE];
int top;
public:
MyStack() {
top = -1;
}
// 入栈
void push(T val) {
if (top >= SIZE - 1) return;
arr[++top] = val;
}
// 出栈
T pop() {
if (top < 0) return T();
return arr[top--];
}
// 判断空
bool isEmpty() {
return top == -1;
}
};
int main() {
// int栈
MyStack<int> st1;
st1.push(10);
st1.push(20);
cout << st1.pop() << endl; // 20
// double栈
MyStack<double> st2;
st2.push(3.14);
cout << st2.pop() << endl; // 3.14
// string栈
MyStack<string> st3;
st3.push("hello");
cout << st3.pop() << endl; // hello
}
示例4:STL 真实通用容器用法
#include <vector>
#include <list>
#include <map>
using namespace std;
// 1. vector 动态数组
vector<int> v1;
vector<string> v2;
// 2. list 链表
list<int> lst;
// 3. map 键值对
map<int, string> m;
m[1] = "张三";
十五、异常处理(try、throw、catch)
1.三个关键字
-
try尝试执行可能出错的代码块
-
throw抛出异常,相当于报警
-
catch捕获并处理异常,相当于接警处理
2.基本语法结构
try {
// 可能出错的代码
// 出错时 throw 抛出异常
}
catch (异常类型1) {
// 处理异常1
}
catch (异常类型2) {
// 处理异常2
}
3.最简单示例:除零异常
#include <iostream>
using namespace std;
int div(int a, int b) {
if (b == 0) {
// 抛出异常
throw "除数不能为零!";
}
return a / b;
}
int main() {
try {
cout << div(10, 2) << endl;
cout << div(10, 0) << endl; // 会出错
}
// 捕获 const char* 类型异常
catch (const char* msg) {
cout << "捕获异常:" << msg << endl;
}
cout << "程序继续正常运行~" << endl;
return 0;
}
4.抛出多种类型异常
可以 throw int、double、string、对象 等任意类型。
void test(int x) {
if (x < 0)
throw -1; // int 异常
if (x == 0)
throw 0.0; // double 异常
if (x > 100)
throw "数值过大"; // const char*
}
捕获:
try {
test(120);
}
catch (int e) {
cout << "int异常:" << e << endl;
}
catch (double e) {
cout << "double异常:" << e << endl;
}
catch (const char* msg) {
cout << "字符串异常:" << msg << endl;
}
5.捕获所有异常(万能 catch)
不管抛什么类型都能接住:
catch (...) {
cout << "发生未知异常!" << endl;
}
... 表示任意类型。
6.异常对象(常用)
可以自定义异常类,更规范:
class MyError {
public:
const char* msg;
MyError(const char* m) : msg(m) {}
};
void test() {
throw MyError("自定义异常");
}
// 捕获
catch (MyError e) {
cout << e.msg << endl;
}
7.栈解旋(重要机制)
当异常抛出时:
- 从 try 到 throw 之间创建的局部对象会自动析构
- 不用手动释放,非常安全
8.标准库异常(实际开发常用)
C++ 自带一套异常类,都继承自 exception
#include <stdexcept>
常用:
runtime_error运行时错误out_of_range越界invalid_argument参数无效bad_allocnew 分配内存失败
示例:
throw runtime_error("运行出错了");
catch (exception& e) {
cout << e.what() << endl;
}
十六、函数指针
函数指针就是:指向函数的指针变量。
本质:把函数当作数据,存它的地址,以后可以通过指针调用这个函数。
1.函数指针怎么定义
语法格式:
返回值类型 (*指针名)(参数列表);
例子:
有一个函数:
int add(int a, int b) {
return a + b;
}
定义能指向它的指针:
int (*p_func)(int, int);
赋值:
p_func = add;
调用:
int res = p_func(10, 20); // 等价于 add(10,20)
完整例子:
#include <iostream>
using namespace std;
int add(int a, int b) {
return a + b;
}
int main() {
// 1. 定义函数指针
int (*p)(int, int);
// 2. 指向函数
p = add;
// 3. 通过指针调用函数
cout << p(2, 3) << endl; // 输出 5
return 0;
}
2.简化写法(typedef)
函数指针写起来很长,用 typedef 简化:
typedef int (*FuncPtr)(int, int);
以后直接用:
FuncPtr p = add;
p(1, 2);
3.函数指针做函数参数(最常用)
用途:回调函数
把一个函数传给另一个函数去执行。
#include <iostream>
using namespace std;
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
// 用函数指针当参数
void calculate(int a, int b, int (*op)(int, int)) {
cout << op(a, b) << endl;
}
int main() {
calculate(10, 5, add); // 15
calculate(10, 5, sub); // 5
return 0;
}
这就是通用回调机制
4.和普通指针的区别
- 普通指针:指向数据(int、char、对象)
- 函数指针:指向代码段里的函数地址
- 不能做
p++这种算术运算(无意义)
5.函数指针数组 + 菜单选择(经典实战案例)
- 定义多个功能函数(增、删、改、查、退出)
- 用函数指针数组把它们存起来
- 根据用户输入的菜单编号,直接调用对应函数
#include <iostream>
using namespace std;
// 1. 定义各个功能函数
void add() {
cout << "执行:添加数据" << endl;
}
void del() {
cout << "执行:删除数据" << endl;
}
void update() {
cout << "执行:修改数据" << endl;
}
void query() {
cout << "执行:查询数据" << endl;
}
void exitSys() {
cout << "退出系统,再见!" << endl;
}
int main() {
// 2. 定义函数指针数组
void (*funcArr[])() = {
add, // 编号 0
del, // 编号 1
update, // 编号 2
query, // 编号 3
exitSys // 编号 4
};
int choice;
while (true) {
// 菜单
cout << "===== 菜单 =====" << endl;
cout << "0. 添加" << endl;
cout << "1. 删除" << endl;
cout << "2. 修改" << endl;
cout << "3. 查询" << endl;
cout << "4. 退出" << endl;
cout << "请输入选择:";
cin >> choice;
if (choice < 0 || choice > 4) {
cout << "输入错误,请重新输入!" << endl;
continue;
}
// 3. 通过函数指针数组调用函数
funcArr[choice]();
cout << endl;
// 选择4就退出循环
if (choice == 4) break;
}
return 0;
}
关键点讲解
1. 函数指针数组定义
void (*funcArr[])();
含义:
funcArr是一个数组- 数组里每个元素是:函数指针
- 这些指针指向:无返回值、无参数的函数
2. 赋值
void (*funcArr[])() = {add, del, update, query, exitSys};
直接把函数名放进去,函数名就是地址。
3. 调用
funcArr[choice]();
funcArr[choice]拿到对应函数指针- 后面加
()就调用该函数
带参数版本(扩展)
如果函数需要参数,比如:
void test(int a) { ... }
函数指针数组写法:
void (*funcArr2[])(int) = { test1, test2, test3 };
// 调用
funcArr2[0](100);
十七、Lambda 表达式
Lambda 就是就地定义的匿名函数,不用单独写函数名,常用于简单逻辑、回调、算法参数。
1.基本语法
[捕获列表] (参数列表) -> 返回值类型 { 函数体 };
简化写法(很多地方可以省略):
[](){};
各部分说明:
1. [捕获列表] —— 最重要
用来访问外部变量,相当于传参。
[] 空:不捕获任何外部变量
[=] 值捕获:外部变量都复制一份(只读)
[&] 引用捕获:直接用外部变量(可修改)
[x, &y] 混合:x 值捕获,y 引用捕获
[this] 在类里捕获当前对象
2. (参数)
和普通函数参数一样,可省略。
3. -> 返回值
可省略,编译器自动推导。只有函数体有多条 return 且类型不一致时才需要写。
4. {} 函数体
正常写代码。
2.示例
auto f = []{ cout << "Hello Lambda" << endl; };
f(); // 调用
带参数:
auto add = [](int a, int b){ return a + b; };
cout << add(1,2);
3.捕获外部变量
1. 值捕获 [=]
int a = 10;
auto f = [=]{ cout << a; };
内部是副本,修改不影响外面。
2. 引用捕获 [&]
int a = 10;
auto f = [&]{ a = 20; };
f();
// a 变成 20
3. 混合捕获
int x=1, y=2;
auto f = [x, &y]{
// x 只读
y = 100;
};
4.配合算法(最常用场景)
#include <vector>
#include <algorithm>
vector<int> v = {3,1,4,2};
// 排序
sort(v.begin(), v.end(), [](int a, int b){
return a < b;
});
// 遍历
for_each(v.begin(), v.end(), [](int x){
cout << x << " ";
});
5.带 mutable(修改值捕获变量)
值捕获默认只读,加 mutable 可改副本:
int a = 10;
auto f = [a]() mutable {
a = 99;
};
外部 a 不变。
6.和函数指针搭配
Lambda 可隐式转为函数指针(无捕获时):
void (*fp)(int) = [](int x){ cout << x; };
fp(10);
十八、封装、继承、多态
1.封装
把属性和方法打包成类,控制访问权限,对外隐藏实现。
一句话:保护内部数据,只暴露必要接口。
1. 访问权限
public:公开,外部可访问private:私有,只有类内部可访问(默认)protected:保护,类内部 + 子类可访问
2. 示例
class Person {
private:
int age; // 私有属性,外部不能直接访问
public:
void setAge(int a) { age = a; }
int getAge() { return age; }
};
作用:
- 安全,防止乱改
- 便于维护
- 接口清晰
2.继承
子类继承父类,拥有父类的属性和方法,实现代码复用。
1. 语法
class 子类 : 继承方式 父类 { ... };
2. 三种继承方式
public公有继承:父类public/protected权限不变protected保护继承:父类都变protectedprivate私有继承:父类都变private
3. 示例
class Person {
public:
void eat() { cout << "吃饭" << endl; }
};
class Student : public Person {
public:
void study() { cout << "学习" << endl; }
};
使用:
Student s;
s.eat(); // 继承自父类
s.study(); // 自己的方法
4. 继承中的构造
- 先构造父类
- 再构造子类析构顺序相反
3.多态
同一个接口,不同对象表现出不同行为。
前提条件
- 有继承关系
- 子类 ** 重写(override)** 父类虚函数
- 父类指针 / 引用指向子类对象
1. 虚函数
在父类函数前加 virtual
virtual void speak() { ... }
2. 示例
class Animal {
public:
virtual void speak() {
cout << "动物叫" << endl;
}
};
class Dog : public Animal {
public:
void speak() override {
cout << "汪汪汪" << endl;
}
};
class Cat : public Animal {
public:
void speak() override {
cout << "喵喵喵" << endl;
}
};
3. 多态使用
void doSpeak(Animal &animal) {
animal.speak();
}
int main() {
Dog d;
Cat c;
doSpeak(d); // 输出汪汪汪
doSpeak(c); // 输出喵喵喵
}
这就是多态:传入不同对象,执行不同逻辑。
4. 纯虚函数 & 抽象类
virtual void speak() = 0;
包含纯虚函数的类叫抽象类,不能实例化,必须由子类实现纯虚函数。
5. 虚析构
父类指针删除子类对象时,为了调用子类析构,父类析构要加 virtual
virtual ~Animal() {}
6010

被折叠的 条评论
为什么被折叠?



