本文覆盖

期末必考所有模块

,从基础语法到面向对象核心,兼顾理论考点与编程实践,适合考前系统复习、查漏补缺

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 编译与运行流程

  1. 预处理:处理#include#define,生成.i文件

  2. 编译:将预处理后的文件编译为汇编语言,生成.s文件

  3. 汇编:将汇编语言转为机器码,生成.o目标文件

  4. 链接:链接库文件,生成.exe可执行文件(Windows)/ 可执行程序(Linux)

二、数据类型与常量变量(必考基础)

2.1 基本数据类型(重中之重)

类型占用字节(32 位系统)取值范围应用场景
char1-128 ~ 127存储字符、ASCII 码
unsigned char10 ~ 255无符号字符、字节数据
int4-2³¹ ~ 2³¹-1整数计算(默认首选)
unsigned int40 ~ 2⁴²-1非负整数(如计数)
long long8-2⁶³ ~ 2⁶³-1大整数(超过 int 范围)
float46~7 位有效数字浮点计算(精度要求低)
double815~16 位有效数字浮点计算(默认首选)
bool1true(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 → false3>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 运算符优先级(必考口诀)

优先级从高到低

  1. 单目运算符:++ -- ! ~(自增、自减、非、按位取反)

  2. 算术运算符:* / %+ -

  3. 移位运算符:<. 关系运算符:> < >= == !=

  4. 位运算符:&^|

  5. 逻辑运算符:&&||

  6. 三目运算符:? :

  7. 赋值运算符:= += -= *= /= %=

  8. 逗号运算符:,(优先级最低)

口诀:单目算术移,关系位逻辑,三目赋值逗

四、流程控制语句(编程题骨架)

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关键字修饰的成员函数

  • 规则:

  1. 基类声明虚函数(virtual void show();

  2. 派生类重写虚函数(函数名、参数列表、返回值类型完全一致)

  3. 基类指针 / 引用指向派生类对象,调用虚函数时执行派生类实现

  • 示例(必考):
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 语法层面易错点

  1. 分号遗漏if()for()do-while()后漏分号(如do{...}while(1)错误,需while(1);

  2. 括号匹配:嵌套if-else、循环语句中括号不匹配(推荐每层缩进 + 明确括号范围)

  3. 变量未初始化:局部变量未赋值直接使用(如int a; cout <;,输出随机值)

  4. 数组下标越界arr[n](数组长度为 n 时,下标最大为 n-1),导致内存错误

  5. 字符串结束符:C 风格字符串未手动添加\0(如char str[5] = {'h','e','l','l','o'};,strlen 计算错误)

15.2 逻辑层面易错点

  1. 运算符混淆===&&&|||(如if(a=5)永远为真)

  2. 循环条件错误for(int i=1; i)(死循环,i 初始为 1,递减后永远小于 n)

  3. 递归缺少出口:递归函数无终止条件(如int fib(int n){return fib(n-1)+fib(n-2);},栈溢出)

  4. 指针操作错误:解引用空指针、野指针(如int *p; *p=10;,程序崩溃)

  5. 继承访问权限:派生类直接访问基类 private 成员(需通过基类公有接口访问)

15.3 面向对象易错点

  1. 构造函数重载冲突:默认构造函数与带参构造函数共存时,无参创建对象报错(需显式定义默认构造)

  2. 拷贝构造函数浅拷贝:类含指针成员时,默认拷贝构造导致双重释放(需自定义深拷贝)

  3. 虚函数重写不匹配:派生类重写虚函数时,函数名 / 参数 / 返回值不一致(失去多态特性)

  4. 基类析构函数未加 virtual:基类指针指向派生类堆对象,delete 时仅调用基类析构(内存泄漏)

  5. 静态成员未初始化:静态成员变量类内声明后,未在类外初始化(链接错误)

十六、期末复习策略与答题技巧

16.1 复习优先级(抓大放小)

  1. 必拿分模块(占比 60%+):基础语法、数据类型、运算符、流程控制、数组与字符串、函数基础

  2. 高分模块(占比 25%+):指针与引用、类与对象、继承与多态、结构体

  3. 拔高模块(占比 15%):模板、文件操作、运算符重载、抽象类

16.2 答题技巧

  1. 选择题 / 填空题
  • 优先做送分题(基础语法、数据类型、运算符优先级)

  • 遇到指针 / 引用题,画图分析内存地址(如int a=10; int *p=&a;,标注 p 存储 a 的地址)

  • 常量相关题,区分宏常量与 const 常量(重点记类型检查、作用域)

  1. 编程题(核心得分点)
  • 审题后先写思路注释(如 “冒泡排序:外层循环控制轮数,内层循环比较交换”)

  • 代码结构清晰:函数封装、注释说明、缩进规范

  • 异常处理:数组下标判断、文件打开成功判断、输入合法性检查

  • 多态题必须满足 3 个条件:基类虚函数、派生类重写、基类指针 / 引用指向派生类对象

  1. 简答题
  • 按 “定义 + 特点 + 示例” 答题(如问函数重载:“同一作用域内函数名相同,参数列表不同→支持多种类型→示例:add (int,int) 与 add (double,double)”)

  • 对比题分点作答(如指针与引用的区别,按本质、初始化、指向修改等维度分点)

16.3 考前冲刺建议

  1. 重点刷编程题:冒泡排序、斐波那契数列、二分查找、学生信息管理、多态图形类(期末高频)

  2. 背记核心结论:运算符优先级口诀、继承访问权限表、指针与引用区别、虚函数多态条件

  3. 模拟考试:限时完成整套真题,训练答题速度(编程题建议每道题控制在 15-20 分钟)

  4. 错题复盘:整理错题本,标注错误原因(如 “数组下标越界”“虚析构函数遗漏”)

更多推荐