本文覆盖

期末必考所有模块

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

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 位系统) 取值范围 应用场景
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 → 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. 错题复盘:整理错题本,标注错误原因(如 “数组下标越界”“虚析构函数遗漏”)

更多推荐