C++面试《葵花宝典》
一、C++语言基础
1.1 C++三大特性
封装、继承、多态
1.2 struct与class
控制权限
- Struct默认访问控制权限为public
- Class默认访问控制权限为private
是否允许有成员函数
- C中struct不允许有函数
- C++中的struct几乎拥有class的一切属性
C 、C++中使用结构体的区别
- C中使用结构体
struct Student stu1
- C++中使用结构体
Student stu1
1.3 include ‘’ ’’ 和 <> 的区别
- 在预编译阶段查找头文件的路径不一样
<>: 编译器设置的头文件路径 -> 系统变量
‘’ ’’:当前头文件目录 -> 编译器设置的头文件路径 -> 系统变量
1.4 导入C函数的关键字extern “C”
- extern “C”
- extern “C” int strcmp(const char *s1, const char s2);
1.5 预编译、编译、汇编、链接
- 预编译:处理#define、#include等预编译指令,将文件插入该位置。过滤所有注释
- 编译:生成汇编代码
- 汇编:将汇编代码转变成可执行的指令
- 链接:将目标文件进行链接,从而形成可执行程序
- 静态链接:在链接的时候就已经吧要调用的函数或过程链接到生成的可执行文件中
- 动态链接:生成的可执行文件没有函数代码,只有重定位信息
1.6 static
内存分配
- 内存分配在堆上,其值可改变
- 对于C而言,static在编译时就分配内存
- 对于C++而言,static在首次使用时分配内存
- 在程序结束时释放内存
使用方式
- 静态函数、静态变量都只能在本源文件中使用
- 类的静态变量、函数被所有对象共用
- 静态成员函数没有this指针,不可以通过非静态成员访问
1.7 数组名与指针
意义
- 数组名是首元素的地址
- 指针是一个变量,其值为一个内存地址
内存大小
- 数组所占的内存大小:sizeof(数组名)/sizeof(数据类型)
- 指针的大小与系统有关,32位是4byte;64位是8byte
1.8 函数指针
- 就是每一个函数的入口地址
- 定义:
int func(int a);
int (*f)(int a);
f = &func;
- 可以用于:使用同一的方式调用不同的方法
1.9 野指针
- 野指针就是指针指向的位置是不正确的,不符合预期的
- 例如,某指针指向的内存被释放后,该指针没有置空,而是继续指向该内存。若这块内存被填入了新的数据,那么指针就出现了非法访问的错误,是野指针
- 规避野指针,可采用智能指针
1.10 malloc和free
malloc函数是一种分配长度为num_bytes字节的内存块的函数,可以向系统申请分配指定size个字节的内存空间
void* malloc(long NumBytes)
- 该函数分配了NumBytes个字节,并返回了指向这块内存的指针。
- 如果分配失败,则返回一个空指针(NULL)。
- 返回类型是 void* 类型。void* 表示未确定类型的指针。
- C,C++规定,void* 类型可以通过类型转换强制转换为任何其它类型的指针。
malloc的用法
include <malloc.h>
char* p = (char*)malloc(100*sizeof(char));
If (p == NULL)
exit(1);
free(P);
P = NULL;
1.11 const与define
- 它们定义的都是常量,其值不可再改变
- define在预处理阶段生效,const在编译阶段生效
- define是宏定义替换,常量不在内存中
- const定义的常量存放在内存中
- define常量不带类型,const常量带类型
1.12 使用指针的注意事项
- 初始化置NULL
- 使用new、malloc分配内存后立刻判空NULL
- 内存申请与释放必须配对
- 指针释放后置NULL
1.13 内联函数inline
- 内联函数在编译时将代码嵌入
- 内联函数不需要寻址,避免了函数调用的开销
- 内联函数要求代码简单,无循环
1.14 C++值传递的方式
“值传递”是说 调用函数传参 的这个过程
- 值传递:形参改变,不影响实参
- 指针传递:形参改变,影响实参
- 应用传递:形参改变,影响实参
二、C++内存
2.1 堆和栈
- 分配空间方式不同。栈由操作系统自动分配释放,堆由程序员主动分配释放
- 缓存方式不同。栈使用一级缓存,调用完毕立即释放。堆使用二级缓存
- 数据结构不同。堆类似数组,栈类似FILO栈结构
2.2 C++的5个内存区
- 堆
- 栈
- 自由存储区:类似堆,由malloc分配
- 全局/静态存储区:存放全局变量和静态变量static
- 常量存储区:存放常量,不允许修改
2.3 常见的内存错误
内存分配失败,但程序员不知
- 初始化置NULL
- 使用new、malloc分配内存后立刻判空NULL,依然为NULL则分配失败
内存未被初始化就被引用
- 要为动态内存或数组赋初值
内存越界
- 注意使用数组时的下标问题
释放内存后,形成了野指针
- 释放内存后,立即将指针置空
内存泄漏
-
内存泄露的两种情况:
- new / malloc申请内存后没有释放
- 继承时,父类析构函数不是虚函数
-
如何避免:
- 养成良好习惯,使用完内存后立即释放
- 将分配内存的指针以链表的形式进行管理,释放内存后从链表删除
- 程序结束后检查链表
- 可以使用智能指针
2.4 为什么父类析构函数一定要是virtual
- 首先,子类在父类的基础上,可能新申请了一些动态内存
- 所以,子类的析构函数要比父类的多一些释放操作
- 我们使用C++继承时,常常用基类指针,指向子类对象,如:Parent* p = new Child();
- 若父类析构函数不为virtual,执行delete p时,调用的就是父类析构函数
- 这会导致少了一些操作,一些子类的内存没有被释放
- 也就是说,父类析构函数不为virtual,会导致内存泄漏
- (但是,C++默认的析构函数不是虚函数)
2.5 程序运行时内存有哪些section
在执行时,从低地址到高地址:
- 代码段:存放代码
- 数据段:已初始化的全局和静态变量
- BSS段:未初始化(或初始化为0)的全局和静态变量
- 堆:动态内存
- 共享区:位于堆栈之间
- 栈:局部变量等
2.6 程序启动的过程
- 首先,操作系统创建进程并分配空间
- 加载器把可执行文件的代码与数据段映射到虚拟内存空间中
- 加载器读入“导入符号表”,从而查找所依赖的动态链接库,并调用
- 初始化应用程序的全局变量或全局对象
- 进入应用程序入口函数,开始执行
2.7 内存对齐
什么是内存对齐
- Struct、class、union的各数据按照一定的规则在空间上排列,而不是简单的顺序存放
为什么要内存对齐
- 提高cpu的访问速率
- 一些平台对内存对齐有严格要求
对齐原则
- Struct、class、union第一个数据成员放置在offset为0的地方
- 以后每个数据成员存储的起始位置,都要从该成员大小的整数倍开始
- Struct的总大小(sizeof),必须是其内部“最宽基本类型成员”的整数倍。不足的要补齐
三、面向对象
3.1 面向对象的三大特征
(就是C++的三大特性)封装、继承、多态
- 封装:将数据与操作数据的方法结合,对外隐藏对象的属性和实现细节,仅提供对外接口用以交互
- 继承:在无需重新编写原来的类的情况下对类的功能进行