C++ 期末考试知识点总结
本文覆盖
期末必考所有模块
,从基础语法到面向对象核心,兼顾理论考点与编程实践,适合考前系统复习、查漏补缺
C++ 期末考试知识点总结,期末试题,学习笔记, 详见:链接
一、C++ 基础语法(送分题核心)
1.1 程序基本结构与规范
#include 输入输出流头文件(必考)
using namespace std; // 标准命名空间,省略std::前缀(必须写)
// 主函数:程序唯一入口(无返回值用void main(),部分编译器支持)
int main() {
cout \<Hello C++" <; // 输出语句(endl换行+刷新缓冲区)
int a;
cin >> a; // 输入语句(接收键盘输入)
return 0; // 正常退出返回0,异常返回非0
}
考点清单:
-
头文件区别:
标准)vs>`(兼容 C 的 stdio.h) -
命名空间:
using namespace std;的作用,避免命名冲突 -
程序执行流程:从
main()开始,必须有且仅有一个 main 函数
1.2 注释与编码规范
-
单行注释:
// 注释内容(常用,编译器忽略) -
多行注释:
/* 注释内容 */(不可嵌套) -
编码规范:变量名用小写 + 下划线(
student_id),常量全大写(MAX_SIZE),类名首字母大写(Person)
1.3 编译与运行流程
-
预处理:处理
#include、#define,生成.i文件 -
编译:将预处理后的文件编译为汇编语言,生成
.s文件 -
汇编:将汇编语言转为机器码,生成
.o目标文件 -
链接:链接库文件,生成
.exe可执行文件(Windows)/ 可执行程序(Linux)
二、数据类型与常量变量(必考基础)
2.1 基本数据类型(重中之重)
| 类型 | 占用字节(32 位系统) | 取值范围 | 应用场景 |
|---|---|---|---|
char |
1 | -128 ~ 127 | 存储字符、ASCII 码 |
unsigned char |
1 | 0 ~ 255 | 无符号字符、字节数据 |
int |
4 | -2³¹ ~ 2³¹-1 | 整数计算(默认首选) |
unsigned int |
4 | 0 ~ 2⁴²-1 | 非负整数(如计数) |
long long |
8 | -2⁶³ ~ 2⁶³-1 | 大整数(超过 int 范围) |
float |
4 | 6~7 位有效数字 | 浮点计算(精度要求低) |
double |
8 | 15~16 位有效数字 | 浮点计算(默认首选) |
bool |
1 | true(1) / false(0) |
逻辑判断 |
关键考点:
-
字符型本质:存储 ASCII 码(
'A'=65、'a'=97、'0'=48) -
浮点型精度:
float精度不足时用double,避免精度误差(如0.1+0.2≠0.3) -
类型长度:
int在 32 位和 64 位系统均为 4 字节,long在 32 位为 4 字节、64 位为 8 字节
2.2 常量与变量
(1)变量定义与初始化
-
定义格式:
类型 变量名 = 初始值;(推荐初始化,避免随机值) -
示例:
int a = 10; double b = 3.14; bool flag = true; -
规则:先定义后使用,同一作用域内变量名唯一
(2)常量(不可修改的量)
| 类型 | 定义格式 | 特点 | 推荐度 |
|---|---|---|---|
| 宏常量 | #define PI 3.14159 |
预处理阶段替换,无类型检查 | ❌ |
| const 常量 | const double PI = 3.14 |
编译期检查,有类型,占内存 | ✅ |
| 枚举常量 | enum Week {MON=1, TUE} |
离散常量集合,默认从 0 开始赋值 | ✅ |
考点:宏常量与 const 常量的区别(类型检查、作用域、内存占用)
2.3 类型转换
(1)隐式转换(自动转换,安全)
-
规则:低精度 → 高精度(不会丢失数据)
-
示例:
int a = 5; double b = a; // a=5 → b=5.0 -
常见场景:算术运算中混合类型自动转换(如
int + double → double)
(2)显式转换(强制转换,需谨慎)
-
C 风格:
(目标类型)变量→int b = (int)3.14; // b=3 -
C++ 风格:
目标类型(变量)→int b = int(3.14);(推荐,更清晰) -
风险:高精度→低精度会丢失数据(如
double 3.99 → int 3)
三、运算符与表达式(计算类题型核心)
3.1 运算符分类与用法
(1)算术运算符
| 运算符 | 作用 | 注意事项 |
|---|---|---|
+ - * |
加、减、乘 | 正常运算 |
/ |
除 | 整数相除取整(5/2=2),浮点相除取小数(5.0/2=2.5) |
% |
取余 | 仅适用于整型(5%2=1),负数取余符号与被除数一致(-5%2=-1) |
++ |
自增 | 前置++a(先增后用),后置a++(先用后增) |
-- |
自减 | 同自增逻辑 |
必考示例:
int a = 5, b = 5;
cout < 6(a先变成6,再输出)
cout <5(先输出b=5,再变成6)
cout << a <; // 6 6
(2)关系运算符
-
运算符:
> < >= - 结果:bool类型(true/false`) -
易错点:
==(相等判断)与=(赋值)混淆(如if(a=5)是赋值,永远为真)
(3)逻辑运算符
-
运算符:
&&(逻辑与)、||(逻辑或)、!(逻辑非) -
短路特性(必考):
-
&&:左侧为false,右侧不执行(如(5>10) && (++a)→a不增) -
||:左侧为true,右侧不执行(如(5<10) || (++a)→a不增)
-
-
示例:
!true → false,3>2 && 4 → true
(4)赋值与复合赋值运算符
-
赋值:
a = 10;(将右侧值赋给左侧变量) -
复合赋值:
a += 5(等价于a = a+5)、a *= 3(等价于a = a*3) -
其他:
-=、/=、%=(仅适用于整型)
(5)三目运算符(条件运算符)
-
格式:
条件 ? 表达式1 : 表达式2 -
逻辑:条件为
true执行表达式 1,否则执行表达式 2 -
示例:
int max = (a>b) ? a : b;(求 a 和 b 的最大值) -
考点:可嵌套(如
(a>b) ? (a>c?a:c) : (b>c?b:c))
3.2 运算符优先级(必考口诀)
优先级从高到低:
-
单目运算符:
++ -- ! ~(自增、自减、非、按位取反) -
算术运算符:
* / %→+ - -
移位运算符:
<. 关系运算符:> < >=== != -
位运算符:
&→^→| -
逻辑运算符:
&&→|| -
三目运算符:
? : -
赋值运算符:
= += -= *= /= %= -
逗号运算符:
,(优先级最低)
口诀:单目算术移,关系位逻辑,三目赋值逗
四、流程控制语句(编程题骨架)
4.1 顺序结构
-
默认执行流程:从上到下、从左到右依次执行
-
示例:变量定义→赋值→运算→输出
4.2 选择结构(分支语句)
(1)if-else 语句
// 单分支
if (条件) {
执行语句; // 条件为true时执行
}
// 双分支
if (条件) {
语句1;
} else {
语句2;
}
// 多分支
if (条件1) {
语句1;
} else if (条件2) {
语句2;
} else {
语句3; // 所有条件都不满足时执行
}
考点:
-
条件表达式必须是
bool类型(或可隐式转为 bool) -
单语句可省略
{},但推荐加上(避免逻辑错误) -
嵌套 if-else:注意缩进,避免歧义(如
if(条件1) if(条件2) 语句; else 语句;)
(2)switch 语句(多分支选择)
switch (表达式) { // 表达式必须是整型/字符型/枚举型
case 常量1:
语句1;
break; // 必须加,否则穿透(执行下一个case)
case 常量2:
语句2;
break;
default: // 可选,所有case不匹配时执行
语句3;
break;
}
必考考点:
-
case 后必须是常量(不能是变量,如
case a:错误) -
缺少
break会导致穿透(如case 1执行后继续执行case 2) -
字符型可直接作为表达式(如
switch(ch),case 'A':)
4.3 循环结构
(1)for 循环(已知循环次数,推荐)
for (初始化表达式; 条件表达式; 更新表达式) {
循环体;
}
-
执行流程:初始化 → 判断条件 → 满足则执行循环体 → 更新 → 重复判断
-
示例:打印 1~10
for (int i=1; i; i++) {
cout << i <
}
-
考点:
-
初始化表达式可省略(需在循环外定义变量)
-
条件表达式省略则为
true(死循环) -
多变量控制:
for(int i=0,j=10; ij--)
-
(2)while 循环(未知循环次数)
while (条件表达式) {
循环体;
更新表达式; // 不可省略,否则死循环
}
- 示例:计算 1~100 的和
int sum = 0, i = 1;
while (i 00) {
sum += i;
i++;
}
(3)do-while 循环(至少执行一次)
do {
循环体;
更新表达式;
} while (条件表达式); // 分号不可省略
- 区别:先执行循环体,再判断条件(适合必须执行一次的场景,如密码输入验证)
4.4 跳转语句
| 语句 | 作用 | 适用场景 |
|---|---|---|
break |
跳出当前循环或 switch 语句 | for/while/do-while/switch |
continue |
跳过本次循环剩余语句,进入下一次循环 | 循环语句 |
return |
结束当前函数,返回值(无返回值用return;) |
函数体内 |
易错点:
-
break只能跳出一层循环(嵌套循环需用标志位) -
continue不终止循环,仅跳过本次迭代
五、数组与字符串(数据存储必考)
5.1 一维数组
(1)定义与初始化
-
定义格式:
类型 数组名[数组长度];(长度必须是常量,不可变) -
初始化方式:
int arr1\[5] = {1,2,3,4,5}; // 完全初始化
int arr2\[5] = {1,2,3}; // 部分初始化,未赋值元素为0
int arr3\[] = {1,2,3,4}; // 省略长度,编译器自动计算(推荐)
- 禁忌:
int n=5; int arr[n];(长度不能是变量,C++ 不支持变长数组)
(2)数组访问与遍历
-
访问方式:
数组名[下标](下标从 0 开始,范围0~长度-1) -
遍历方式:for 循环(下标遍历)
int arr\[5] = {1,2,3,4,5};
for (int i=0; i<5; i++) {
cout <] <输出1 2 3 4 5
}
- 易错点:下标越界(如
arr[5],超出数组范围,导致内存错误)
5.2 二维数组
(1)定义与初始化
-
定义格式:
类型 数组名[行数][列数];(列数不可省略) -
初始化方式:
int arr1\[2]\[3] = {{1,2,3}, {4,5,6}}; // 完全初始化
int arr2\[2]\[3] = {1,2,3,4,5,6}; // 简化初始化
int arr3\[]\[3] = {{1,2}, {3,4}}; // 省略行数,编译器自动计算
(2)遍历方式(双层 for 循环)
int arr\[2]\[3] = {{1,2,3}, {4,5,6}};
for (int i=0; i<2; i++) { // 外层循环控制行
for (int j=0; j; j++) { // 内层循环控制列
cout <] < }
cout <;
}
5.3 字符串(C 风格 vs C++ 风格)
(1)C 风格字符串(字符数组)
-
定义:
char 字符串名[长度] = "字符串内容";(末尾自动添加\0结束符) -
示例:
char str[6] = "hello";(实际存储:h e l l o \0) -
常用函数(头文件 `
-
strlen(str):获取字符串长度(不含\0) -
strcpy(str1, str2):将 str2 复制到 str1 -
strcat(str1, str2):将 str2 拼接在 str1 后 -
strcmp(str1, str2):比较字符串(相等返回 0,str1>str2 返回正,否则负)
-
(2)C++ 风格 string 类(推荐,必考)
-
头文件:
#include 定义与初始化:string str = “hello”;` -
常用操作(无需记函数,直观易懂):
string s1 = "hello", s2 = "world";
s1.length(); // 5(获取长度)
s1 + s2; // "helloworld"(拼接)
s1 == s2; // false(直接比较,无需strcmp)
s1\[0]; // 'h'(下标访问)
s1.substr(1,3); // "ell"(从下标1开始,取3个字符)
cin >> s1; // 输入字符串(遇空格停止)
getline(cin, s1); // 输入整行字符串(含空格)
- 考点:string 类与 C 风格字符串的转换(
c_str()方法)
string s = "hello";
const char \*cstr = s.c\_str(); // 转为C风格字符串
六、函数(模块化编程核心)
6.1 函数的定义、声明与调用
(1)函数定义(实现)
返回值类型 函数名(参数列表) {
函数体;
return 返回值; // 与返回值类型一致,void无需return
}
- 示例:求两数之和
int add(int a, int b) { // a、b为形参(形式参数)
return a + b;
}
(2)函数声明(原型)
-
作用:告诉编译器函数的名称、返回值类型、参数列表,无需实现
-
格式:
返回值类型 函数名(参数列表);(分号结尾) -
位置:通常放在头文件或主函数之前(必须在调用之前声明)
-
示例:
int add(int a, int b);
(3)函数调用
-
格式:
函数名(实参列表);(实参需与形参类型、个数、顺序一致) -
示例:
int main() {
int x=10, y=20;
int result = add(x, y); // x、y为实参(实际参数)
cout <30
return 0;
}
6.2 参数传递方式(必考重点)
| 传递方式 | 定义格式 | 特点(是否影响实参) | 适用场景 |
|---|---|---|---|
| 值传递 | void fun(int a) |
不影响(形参是实参副本) | 简单类型、不修改实参 |
| 引用传递 | void fun(int &a) |
影响(形参是实参别名) | 修改实参、避免拷贝 |
| 地址传递 | void fun(int *a) |
影响(传递实参地址) | 数组、指针操作 |
示例对比:
// 值传递(不修改实参)
void swap1(int a, int b) {
int temp = a; a = b; b = temp;
}
// 引用传递(修改实参,推荐)
void swap2(int \&a, int \&b) {
int temp = a; a = b; b = temp;
}
// 地址传递(修改实参)
void swap3(int \*a, int \*b) {
int temp = \*a; \*a = \*b; \*b = temp;
}
int main() {
int x=1, y=2;
swap1(x,y); // x=1,y=2(无变化)
swap2(x,y); // x=2,y=1(变化)
swap3(\&x,\&y); // x=1,y=2(变化,需传地址)
return 0;
}
6.3 函数重载(静态多态)
-
定义:同一作用域内,函数名相同,参数个数、类型、顺序不同(返回值类型无关)
-
示例:
int add(int a, int b) { return a+b; }
double add(double a, double b) { return a+b; }
int add(int a, int b, int c) { return a+b+c; }
- 考点:函数重载的匹配规则(精确匹配→隐式转换匹配→不匹配报错)
6.4 特殊函数
(1)默认参数
-
定义:函数声明时给参数赋默认值,调用时可省略该实参
-
规则:默认参数从右往左定义(不能跳过左侧参数给右侧赋默认值)
-
示例:
int fun(int a, int b=10, int c=20); // 正确
// int fun(int a=10, int b, int c); // 错误(左侧参数不能先有默认值)
- 调用:
fun(5);(a=5,b=10,c=20)、fun(5, 15);(a=5,b=15,c=20)
(2)内联函数
-
定义:
inline 返回值类型 函数名(参数列表) { 函数体; } -
作用:编译器将函数体直接嵌入调用处,减少函数调用开销(适合短小函数)
-
考点:内联函数与宏定义的区别(内联函数有类型检查,宏定义无)
(3)递归函数
-
定义:函数自身调用自身
-
必要条件:① 递归出口(终止条件);② 递归表达式(逐步逼近出口)
-
示例:求 n 的阶乘(n! = n*(n-1)!)
int factorial(int n) {
if (n == 1) return 1; // 递归出口
return n \* factorial(n-1); // 递归表达式
}
- 考点:递归调用栈(避免栈溢出,n 不宜过大)
6.5 变量作用域与存储类型
| 变量类型 | 定义位置 | 作用域 | 生命周期 | 存储区域 |
|---|---|---|---|---|
| 局部变量 | 函数 / 代码块内 | 所在函数 / 代码块 | 函数调用开始→结束 | 栈区 |
| 全局变量 | 函数外 | 整个程序 | 程序运行开始→结束 | 全局 / 静态区 |
| 静态变量 | 函数内 / 外(static 修饰) | 所在函数 / 全局 | 程序运行开始→结束 | 全局 / 静态区 |
考点:
-
静态局部变量:
static int a=10;(仅初始化一次,下次调用保留上次值) -
全局变量与局部变量同名:局部变量优先(作用域隐藏)
七、指针与引用(难点,高分关键)
7.1 指针基础
(1)指针定义与本质
-
定义:
类型 *指针名;(指针是存储变量内存地址的变量) -
核心操作:
-
取地址:
&变量名(获取变量的内存地址) -
解引用:
*指针名(通过指针访问变量的值)
-
-
示例:
int a = 10;
int \*p = \&a; // p存储a的地址(p是指针变量)
cout << \&a; // 输出a的地址(如0x7ffeefbff57c)
cout < 输出p的值(即a的地址,与上一行相同)
cout << \*p; // 输出10(通过p访问a的值)
\*p = 20; // 修改a的值为20(a=20)
(2)指针的常见状态
-
空指针:
int *p = NULL;(C++11 推荐nullptr)→ 不指向任何有效内存,禁止解引用 -
野指针:未初始化的指针(如
int *p;)→ 指向随机地址,极度危险 -
悬空指针:指向已释放内存的指针 → 禁止解引用
7.2 指针与数组(紧密关联)
-
核心结论:数组名是数组首元素的地址(
arr == &arr[0]) -
指针访问数组元素:
int arr\[5] = {1,2,3,4,5};
int \*p = arr; // p指向arr\[0]
cout \<p; // 1(arr\[0])
cout <1); // 2(arr\[1])
cout < // 3(等价于\*(p+2),arr\[2])
- 指针移动:
p++(指向数组下一个元素,移动步长 = 数组元素类型字节数)
7.3 指针与函数
-
指针作为函数参数:实现地址传递(修改实参)
-
函数指针:指向函数的指针(存储函数的入口地址)
int add(int a, int b) { return a+b; }
int (\*p)(int, int) = add; // p是函数指针,指向add函数
cout <(2,3); // 5(调用add(2,3))
7.4 引用(C++ 新增,重点)
(1)引用定义与特性
-
定义:
类型 &引用名 = 变量名;(引用是变量的别名,与变量共享同一块内存) -
核心特性:
-
必须初始化(
int &b;错误) -
一旦绑定,不能修改指向(
int &b=a; int c=20; b=c;是修改 a 的值为 20,不是改变绑定) -
引用本身不占内存(
sizeof(b) == sizeof(a))
-
(2)指针与引用的区别(必考)
| 对比维度 | 指针 | 引用 |
|---|---|---|
| 本质 | 独立变量,存储内存地址 | 变量别名,无独立内存 |
| 初始化 | 可空(NULL/nullptr) |
必须初始化,不能为空 |
| 指向修改 | 可修改(p=&b) |
不可修改,一旦绑定固定 |
| 解引用 | 需用*(*p) |
直接使用(b等价于a) |
| 多级嵌套 | 支持多级指针(int **p) |
不支持多级引用(int &&b是右值引用,非多级) |
八、结构体与共用体(自定义复合类型)
8.1 结构体(struct)
(1)结构体定义与初始化
- 定义:封装不同类型的数据(实现复杂数据类型)
struct Student { // 结构体名Student(自定义类型)
int id; // 成员变量
string name;
float score;
};
- 初始化:
Student s1 = {101, "张三", 90.5}; // 按顺序初始化
Student s2 = {.name="李四", .id=102, .score=88.0}; // 指定成员初始化(C++11+)
(2)结构体成员访问
-
普通对象:
对象名.成员名→s1.name -
指针对象:
指针名->成员名→Student *p=&s1; p->id
(3)结构体数组与结构体指针
-
结构体数组:
Student arr[2] = {{101,"张三"}, {102,"李四"}}; -
结构体指针遍历数组:
Student \*p = arr;
for (int i=0; i {
cout <->name < p++; // 指向 next 结构体
}
8.2 共用体(union)
- 定义:所有成员共享同一块内存(同一时间只能有一个成员有效)
union Data {
int a;
char b;
double c;
};
-
特性:共用体大小 = 最大成员的大小(
sizeof(Data) == 8,因 double 占 8 字节) -
应用场景:节省内存(如数据在不同时刻有不同类型)
九、类与对象(面向对象核心,高分重点)
9.1 类的定义与访问控制(封装特性)
class Person { // class关键字定义类
private: // 私有成员(默认权限):仅类内访问
string name;
int age;
protected: // 保护成员:类内+派生类访问
string id;
public: // 公有成员:类内+类外访问(对外接口)
// 成员函数(方法)
void setName(string n) {
name = n; // 类内可访问私有成员
}
string getName() {
return name;
}
};
封装思想:隐藏私有成员(数据),通过公有成员函数(接口)访问和修改,保证数据安全性
9.2 对象的创建与销毁
(1)对象创建(实例化)
-
栈对象:
Person p;(自动分配内存,生命周期结束自动销毁) -
堆对象:
Person *p = new Person;(手动分配内存,需用delete销毁)
Person \*p = new Person;
p->setName("张三"); // 堆对象用->访问成员
delete p; // 释放内存,避免内存泄漏
p = nullptr; // 避免悬空指针
(2)构造函数(初始化对象)
-
定义:函数名与类名相同,无返回值,可重载
-
分类:
class Person {
public:
// 1. 默认构造函数(无参)
Person() {
name = "未知";
age = 0;
}
// 2. 带参构造函数
Person(string n, int a) {
name = n;
age = a;
}
// 3. 拷贝构造函数(用已有对象初始化新对象)
Person(const Person \&other) {
name = other.name;
age = other.age;
}
private:
string name;
int age;
};
-
调用时机:
-
创建对象时自动调用(
Person p("张三",20);) -
拷贝初始化时调用(
Person p2 = p1;) -
函数参数为值传递时调用
-
函数返回值为对象时调用
-
(3)析构函数(释放资源)
-
定义:
~类名(),无参无返回值,不能重载(一个类仅有一个) -
作用:对象销毁时自动调用,释放动态内存、文件句柄等资源
-
示例:
class Person {
public:
Person() { cout <函数调用" < }
\~Person() { cout <函数调用" < }
};
- 调用顺序:栈对象→先创建后销毁;堆对象→
delete时销毁
9.3 this 指针(隐含指针)
-
本质:每个成员函数都有一个隐含的
this指针,指向当前对象 -
作用:区分成员变量与局部变量(同名时)
void setName(string name) {
this->name = name; // this->name指成员变量,name指参数
}
- 特性:
this指针不可修改,在常成员函数中为const Person *this(不能修改成员变量)
9.4 常成员与常对象
- 常成员变量:
const 类型 变量名;(必须在构造函数初始化列表中初始化)
class Person {
private:
const int id; // 常成员变量
public:
// 初始化列表初始化常成员变量
Person(int i) : id(i) {}
};
- 常成员函数:
返回值类型 函数名() const;(不能修改成员变量)
string getName() const {
// name = "李四"; // 错误,不能修改成员变量
return name;
}
- 常对象:
const Person p;(只能调用常成员函数,不能修改成员变量)
9.5 静态成员(static)
-
静态成员变量:
-
定义:
static 类型 变量名;(所有对象共享,类外初始化) -
访问:
类名::变量名或对象名.变量名
-
class Person {
public:
static int count; // 静态成员变量(统计对象个数)
};
int Person::count = 0; // 类外初始化(必须)
-
静态成员函数:
-
定义:
static 返回值类型 函数名();(只能访问静态成员变量和静态成员函数) -
访问:
类名::函数名()或对象名.函数名()
-
9.6 友元(破坏封装,特例)
- 友元函数:外部函数可访问类的私有成员
class Person {
friend void printName(Person \&p); // 友元函数声明
private:
string name;
};
void printName(Person \&p) {
cout < 友元函数可访问私有成员
}
- 友元类:一个类可访问另一个类的私有成员
class A {
friend class B; // B是A的友元类,B可访问A的私有成员
private:
int x;
};
十、继承与派生(面向对象核心,必考)
10.1 继承的定义与格式
-
定义:派生类(子类)继承基类(父类)的成员,实现代码复用
-
格式:
class 派生类名 : 继承方式 基类名 { 派生类成员 }; -
示例:
class Person { // 基类
public:
string name;
int age;
void eat() { cout <" <};
class Student : public Person { // 派生类Student,公有继承Person
public:
int id; // 派生类新增成员
void study() { cout < <
- 派生类访问基类成员:直接访问公有 / 保护成员,私有成员不可访问
10.2 继承方式与访问权限(必考)
| 继承方式 | 基类 public 成员 | 基类 protected 成员 | 基类 private 成员 |
|---|---|---|---|
| public(公有继承) | 派生类 public | 派生类 protected | 不可访问 |
| protected(保护继承) | 派生类 protected | 派生类 protected | 不可访问 |
| private(私有继承) | 派生类 private | 派生类 private | 不可访问 |
核心结论:
-
基类 private 成员无论何种继承方式,派生类都无法直接访问(只能通过基类公有接口访问)
-
公有继承是最常用的(保持基类接口的访问权限)
10.3 派生类的构造与析构(必考)
(1)构造函数调用顺序
-
规则:基类构造函数 → 派生类成员对象构造函数 → 派生类构造函数
-
示例:
class Person {
public:
Person() { cout <" <};
class Student : public Person {
public:
Student() { cout << "Student构造" <; }
};
Student s; // 输出:Person构造 → Student构造
- 派生类构造函数初始化基类:通过初始化列表
class Person {
public:
Person(string n) : name(n) {}
private:
string name;
};
class Student : public Person {
public:
// 初始化列表调用基类带参构造函数
Student(string n, int id) : Person(n), id(id) {}
private:
int id;
};
(2)析构函数调用顺序
-
规则:派生类析构函数 → 派生类成员对象析构函数 → 基类析构函数(与构造顺序相反)
-
示例:
class Person {
public:
\~Person() { cout <析构" < }
};
class Student : public Person {
public:
\~Student() { cout <构" <
};
Student s; // 输出:Student析构 → Person析构
10.4 继承中的同名隐藏与覆盖
-
同名隐藏:派生类成员与基类成员同名,派生类成员隐藏基类成员(通过
基类名::成员名访问基类成员) -
示例:
class Person {
public:
void show() { cout < <
class Student : public Person {
public:
void show() { cout << "Student" < } // 隐藏基类show()
};
Student s;
s.show(); // Student(调用派生类)
s.Person::show(); // Person(调用基类)
10.5 多继承与二义性
-
多继承:一个派生类继承多个基类(
class C : public A, public B) -
二义性:多个基类有同名成员,派生类访问时歧义(需指定基类名)
-
解决:虚基类(
class A : virtual public Base),避免基类多次继承
十一、多态与虚函数(面向对象核心,难点 + 高分)
11.1 多态的概念与分类
-
定义:同一接口(函数名),不同实现(函数体),根据对象类型动态选择执行
-
分类:
-
静态多态:编译期确定(函数重载、运算符重载)
-
动态多态:运行期确定(虚函数实现,必考)
-
11.2 虚函数(动态多态核心)
(1)虚函数定义与使用
-
定义:基类中用
virtual关键字修饰的成员函数 -
规则:
-
基类声明虚函数(
virtual void show();) -
派生类重写虚函数(函数名、参数列表、返回值类型完全一致)
-
基类指针 / 引用指向派生类对象,调用虚函数时执行派生类实现
- 示例(必考):
class Base {
public:
virtual void show() { // 虚函数
cout << "Base类" << endl;
}
};
class Derive : public Base {
public:
void show() override { // 重写虚函数(override关键字可选,显式声明)
cout <" < }
};
int main() {
Base \*p = new Derive; // 基类指针指向派生类对象
p->show(); // 输出Derive类(动态多态,运行期确定)
delete p;
return 0;
}
(2)虚函数表(原理,理解即可)
-
基类声明虚函数后,编译器为类生成虚函数表(存储虚函数地址)
-
每个对象有一个虚指针(vptr),指向虚函数表
-
派生类重写虚函数后,覆盖虚函数表中对应的基类虚函数地址
-
调用时通过虚指针查找虚函数表,执行对应函数
11.3 纯虚函数与抽象类(必考)
-
纯虚函数:
virtual 返回值类型 函数名() = 0;(无函数体,强制派生类重写) -
抽象类:包含纯虚函数的类(不能实例化对象,只能作为基类)
-
示例:
class Shape { // 抽象类
public:
virtual double area() = 0; // 纯虚函数
};
class Circle : public Shape {
private:
double r;
public:
Circle(double r) : r(r) {}
double area() override { // 必须重写纯虚函数
return 3.14 \* r \* r;
}
};
// Shape s; // 错误,抽象类不能实例化
Circle c(2);
cout < // 12.56
11.4 虚析构函数(必考)
-
定义:
virtual ~类名() {} -
作用:基类指针指向派生类堆对象时,释放派生类资源(避免内存泄漏)
-
示例:
class Base {
public:
virtual \~Base() { cout < <虚析构
};
class Derive : public Base {
public:
\~Derive() { cout <" <};
Base \*p = new Derive;
delete p; // 输出:Derive析构 → Base析构(正确释放)
- 结论:只要有派生类,基类析构函数建议定义为虚析构
11.5 运算符重载(静态多态)
-
定义:重新定义运算符的行为,使自定义类型支持运算符操作
-
格式:
返回值类型 operator运算符(参数列表) -
示例:重载
+运算符,实现两个Person对象的年龄相加
class Person {
private:
int age;
public:
Person(int a) : age(a) {}
// 成员函数重载+
Person operator+(const Person \&p) {
return Person(this->age + p.age);
}
int getAge() { return age; }
};
Person p1(10), p2(20);
Person p3 = p1 + p2; // 等价于p1.operator+(p2)
cout <(); // 30
- 常用重载运算符:
+ - * / == != <(<>>` 常用全局函数重载)
十二、模板(泛型编程)
12.1 函数模板
-
定义:通用函数,支持多种数据类型(避免重复编写同名函数)
-
格式:
template 声明/定义(class可替换为typename) -
示例:通用加法函数
template T>
T add(T a, T b) {
return a + b;
}
int main() {
cout << add(1,2); // 3(int类型)
cout <(3.14, 2.5); // 5.64(double类型)
return 0;
}
12.2 类模板
-
定义:通用类,支持多种数据类型
-
格式:
template 类名 { 类成员 }; -
示例:通用栈类
template >
class Stack {
private:
T \*data;
int top;
public:
Stack(int size) {
data = new T\[size];
top = -1;
}
void push(T val) { data\[++top] = val; }
T pop() { return data\[top--]; }
};
int main() {
Stack intStack(5); // int类型栈
intStack.push(10);
StackStack(5); // double类型栈
doubleStack.push(3.14);
return 0;
}
十三、文件操作(实操题型)
13.1 文件流类与头文件
-
头文件:`#include
-
核心类:
-
ofstream:输出文件流(写文件) -
ifstream:输入文件流(读文件) -
fstream:输入输出文件流(读写文件)
-
13.2 文件打开与关闭
-
打开方式:
文件流对象.open("文件路径", 打开模式); -
常用打开模式:
-
ios::in:读模式(ifstream 默认) -
ios::out:写模式(ofstream 默认,覆盖原有内容) -
ios::app:追加模式(在文件末尾写) -
ios::binary:二进制模式(读写二进制文件)
-
-
关闭文件:
文件流对象.close();(必须关闭,释放资源)
13.3 文本文件读写(必考)
(1)写文本文件
\#include >
using namespace std;
int main() {
ofstream ofs;
ofs.open("test.txt", ios::out); // 打开文件
if (!ofs.is\_open()) { // 判断是否打开成功
cout <文件打开失败" <;
return 0;
}
ofs <张三" < ofs <:20" <;
ofs.close(); // 关闭文件
return 0;
}
(2)读文本文件
\#include \<fstream>
\#include \<string>
using namespace std;
int main() {
ifstream ifs;
ifs.open("test.txt", ios::in);
if (!ifs.is\_open()) {
cout <失败" < return 0;
}
// 方式1:char数组
char buf\[1024] = {0};
while (ifs >> buf) { // 遇空格/换行停止
cout << buf <;
}
// 方式2:string
string str;
while (getline(ifs, str)) { // 读取整行
cout << str <
}
ifs.close();
return 0;
}
13.4 二进制文件读写
-
写二进制文件:
write((char*)&对象, sizeof(对象)); -
读二进制文件:
read((char*)&对象, sizeof(对象)); -
示例:存储和读取 Student 对象
struct Student {
int id;
char name\[20];
float score;
};
// 写
ofstream ofs("student.dat", ios::out | ios::binary);
Student s = {101, "张三", 90.5};
ofs.write((char\*)\&s, sizeof(s));
ofs.close();
// 读
ifstream ifs("student.dat", ios::in | ios::binary);
Student s2;
ifs.read((char\*)\&s2, sizeof(s2));
cout << s2.id < " << s2.name << " " < <();
十四、期末必考编程题型(实战演练)
14.1 基础算法题
(1)冒泡排序(对数组升序排序)
void bubbleSort(int arr\[], int n) {
for (int i=0; i\<n-1; i++) {
bool flag = false; // 优化:无交换则有序
for (int j=0; j; j++) {
if (arr\[j] > arr\[j+1]) {
swap(arr\[j], arr\[j+1]);
flag = true;
}
}
if (!flag) break;
}
}
(2)斐波那契数列(递归 + 迭代)
// 迭代(推荐,无栈溢出)
int fib(int n) {
if (n return 1;
int a=1, b=1, c;
for (int i=3; i i++) {
c = a + b;
a = b;
b = c;
}
return c;
}
// 递归(简单但效率低)
int fibRecursion(int n) {
if (n ) return 1;
return fibRecursion(n-1) + fibRecursion(n-2);
}
(3)二分查找(有序数组查找目标值)
int binarySearch(int arr\[], int n, int target) {
int left=0, right=n-1;
while (left ) {
int mid = (left + right) / 2;
if (arr\[mid] == target) return mid;
else if (arr\[mid] > target) right = mid -1;
else left = mid +1;
}
return -1; // 未找到
}
14.2 结构体 / 类编程题
(1)学生信息管理(结构体)
#include #include namespace std;
struct Student {
int id;
string name;
float score;
};
// 输入学生信息
void inputStudent(Student arr\[], int n) {
for (int i=0; i i++) {
cout << "输入第" << i+1 <学生信息(学号 姓名 成绩):";
cin >> arr\[i].id >> arr\[i].name >> arr\[i].score;
}
}
// 输出学生信息
void outputStudent(Student arr\[], int n) {
cout <信息列表:" <;
for (int i=0; i; i++) {
cout < < <" <
<:" \<score < }
}
// 按成绩排序(降序)
void sortByScore(Student arr\[], int n) {
for (int i=0; i\<n-1; i++) {
for (int j=0; j-1-i; j++) {
if (arr\[j].score +1].score) {
swap(arr\[j], arr\[j+1]);
}
}
}
}
int main () {
int n;
cout <:";
cin >> n;
Student arr [n]; // 结构体数组(部分编译器支持变长数组,或用动态内存:Student *arr = new Student [n];)
inputStudent (arr, n);
sortByScore (arr, n);
outputStudent (arr, n);
// 若用动态内存,需添加:delete [] arr;
return 0;
}
(2)图形类多态(面向对象必考)
#include using namespace std;
// 抽象基类(含纯虚函数)
class Shape {
public:
virtual double area() = 0; // 纯虚函数,计算面积
virtual double perimeter() = 0; // 纯虚函数,计算周长
virtual void showInfo() = 0; // 纯虚函数,显示信息
};
// 圆形类(派生类)
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
double area() override {
return 3.14159 \* radius \* radius;
}
double perimeter() override {
return 2 \* 3.14159 \* radius;
}
void showInfo() override {
cout < 半径:" <
<:" < <" << perimeter() <;
}
};
// 矩形类(派生类)
class Rectangle : public Shape {
private:
double width, height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
double area() override {
return width \* height;
}
double perimeter() override {
return 2 \* (width + height);
}
void showInfo() override {
cout << "矩形 - 宽:" << width <高:" < <" < < << perimeter() <
}
};
int main() {
Shape \*shapes\[2]; // 基类指针数组
shapes\[0] = new Circle(3.0); // 指向圆形对象
shapes\[1] = new Rectangle(4.0, 5.0); // 指向矩形对象
// 多态调用:同一接口,不同实现
for (int i=0; i {
shapes\[i]->showInfo();
delete shapes\[i]; // 虚析构函数确保资源释放
}
return 0;
}
14.3 文件操作编程题(实操必考)
(1)学生信息写入文件并读取
\#include >
\#include >
\#include
using namespace std;
struct Student {
int id;
string name;
float score;
};
// 写入学生信息到文件
void writeToFile(Student arr\[], int n, const string \&filename) {
ofstream ofs(filename, ios::out | ios::binary); // 二进制写入
if (!ofs.is\_open()) {
cout <" < return;
}
ofs.write((char\*)arr, sizeof(Student) \* n); // 批量写入
ofs.close();
cout << "信息已写入文件:" << filename <
}
// 从文件读取学生信息
void readFromFile(Student arr\[], int n, const string \&filename) {
ifstream ifs(filename, ios::in | ios::binary);
if (!ifs.is\_open()) {
cout <失败!" <
return;
}
ifs.read((char\*)arr, sizeof(Student) \* n); // 批量读取
ifs.close();
cout <文件读取" <
}
int main() {
int n = 2;
Student arr\[2] = {{101, "张三", 90.5}, {102, "李四", 88.0}};
string filename = "students.dat";
// 写入文件
writeToFile(arr, n, filename);
// 读取文件(验证)
Student arr2\[2];
readFromFile(arr2, n, filename);
for (int i=0; i++) {
cout << "学号:" <2\[i].id
< <
<:" <].score < }
return 0;
}
14.4 模板编程题(泛型编程考点)
(1)通用排序模板(支持任意可比较类型)
\#include #include namespace std;
// 模板函数:冒泡排序(通用类型)
template \<class T>
void bubbleSort(T arr\[], int n) {
for (int i=0; i\<n-1; i++) {
bool flag = false;
for (int j=0; j\<n-1-i; j++) {
if (arr\[j] > arr\[j+1]) { // 需支持>运算符
swap(arr\[j], arr\[j+1]);
flag = true;
}
}
if (!flag) break;
}
}
int main() {
// 测试int类型
int intArr\[] = {3,1,4,1,5};
int n1 = sizeof(intArr)/sizeof(int);
bubbleSort(intArr, n1);
cout <:";
for (int i=0; i\<n1; i++) cout << intArr\[i] << " ";
cout << endl;
// 测试double类型
double doubleArr\[] = {3.14, 1.59, 2.65, 0.78};
int n2 = sizeof(doubleArr)/sizeof(double);
bubbleSort(doubleArr, n2);
cout <:";
for (int i=0; i\<n2; i++) cout << doubleArr\[i] << " ";
cout << endl;
// 测试string类型
string strArr\[] = {"banana", "apple", "orange", "grape"};
int n3 = sizeof(strArr)/sizeof(string);
bubbleSort(strArr, n3);
cout <数组排序后:";
for (int i=0; i i++) cout <\[i] < cout < return 0;
}
十五、期末易错点与避坑指南(保命必备)
15.1 语法层面易错点
-
分号遗漏:
if()、for()、do-while()后漏分号(如do{...}while(1)错误,需while(1);) -
括号匹配:嵌套
if-else、循环语句中括号不匹配(推荐每层缩进 + 明确括号范围) -
变量未初始化:局部变量未赋值直接使用(如
int a; cout <;,输出随机值) -
数组下标越界:
arr[n](数组长度为 n 时,下标最大为 n-1),导致内存错误 -
字符串结束符:C 风格字符串未手动添加
\0(如char str[5] = {'h','e','l','l','o'};,strlen 计算错误)
15.2 逻辑层面易错点
-
运算符混淆:
==与=、&&与&、||与|(如if(a=5)永远为真) -
循环条件错误:
for(int i=1; i)(死循环,i 初始为 1,递减后永远小于 n) -
递归缺少出口:递归函数无终止条件(如
int fib(int n){return fib(n-1)+fib(n-2);},栈溢出) -
指针操作错误:解引用空指针、野指针(如
int *p; *p=10;,程序崩溃) -
继承访问权限:派生类直接访问基类 private 成员(需通过基类公有接口访问)
15.3 面向对象易错点
-
构造函数重载冲突:默认构造函数与带参构造函数共存时,无参创建对象报错(需显式定义默认构造)
-
拷贝构造函数浅拷贝:类含指针成员时,默认拷贝构造导致双重释放(需自定义深拷贝)
-
虚函数重写不匹配:派生类重写虚函数时,函数名 / 参数 / 返回值不一致(失去多态特性)
-
基类析构函数未加 virtual:基类指针指向派生类堆对象,delete 时仅调用基类析构(内存泄漏)
-
静态成员未初始化:静态成员变量类内声明后,未在类外初始化(链接错误)
十六、期末复习策略与答题技巧
16.1 复习优先级(抓大放小)
-
必拿分模块(占比 60%+):基础语法、数据类型、运算符、流程控制、数组与字符串、函数基础
-
高分模块(占比 25%+):指针与引用、类与对象、继承与多态、结构体
-
拔高模块(占比 15%):模板、文件操作、运算符重载、抽象类
16.2 答题技巧
- 选择题 / 填空题:
-
优先做送分题(基础语法、数据类型、运算符优先级)
-
遇到指针 / 引用题,画图分析内存地址(如
int a=10; int *p=&a;,标注 p 存储 a 的地址) -
常量相关题,区分宏常量与 const 常量(重点记类型检查、作用域)
- 编程题(核心得分点):
-
审题后先写思路注释(如 “冒泡排序:外层循环控制轮数,内层循环比较交换”)
-
代码结构清晰:函数封装、注释说明、缩进规范
-
异常处理:数组下标判断、文件打开成功判断、输入合法性检查
-
多态题必须满足 3 个条件:基类虚函数、派生类重写、基类指针 / 引用指向派生类对象
- 简答题:
-
按 “定义 + 特点 + 示例” 答题(如问函数重载:“同一作用域内函数名相同,参数列表不同→支持多种类型→示例:add (int,int) 与 add (double,double)”)
-
对比题分点作答(如指针与引用的区别,按本质、初始化、指向修改等维度分点)
16.3 考前冲刺建议
-
重点刷编程题:冒泡排序、斐波那契数列、二分查找、学生信息管理、多态图形类(期末高频)
-
背记核心结论:运算符优先级口诀、继承访问权限表、指针与引用区别、虚函数多态条件
-
模拟考试:限时完成整套真题,训练答题速度(编程题建议每道题控制在 15-20 分钟)
-
错题复盘:整理错题本,标注错误原因(如 “数组下标越界”“虚析构函数遗漏”)
更多推荐
所有评论(0)