1.进程/线程/协程
进程与线程:
- 进程是资源分配的最小单位,线程是资源调度的最小单位。
- 资源拥有:进程拥有独立的地址空间。而统一进程下的线程共享地址空间和资源,包括内存/文件句柄等。
- 通信方式:进程通信有特定的机制,比如管道/共享内存/消息队列/信号等方式。而线程因为内存等资源是共享的,所以可以通过直接读写全局变量等就可以通信
- 上下文切换:进程切换的开销远大于线程,因为进程切换涉及到了虚拟地址的切换。同样的道理,进程创建的成本也是远高于线程的。
- 错误影响:得益于内存隔离,单一进程崩溃不会直接影响到其他进程。而线程又可能会对其他线程造成直接影响。
协程
协程可以看作是完全作用在用户态的线程,主要用于减少成本,提高并发性。
2.堆/栈
- 内存管理:栈内存由操作系统管理分配,而堆为程序员手动分配。
- 数据结构:栈遵循先入后出原则,而堆的结构较复杂多样。(堆的结构如最大堆/最小堆等也是考察重点!)
- 性能:堆的大小远大于栈,但栈的访问速度比堆快很多。
- 服务对象:栈主要储存局部变量/函数调用/表达式求值,而堆用于动态内存分配/数据持久化/对象存储等
3.指针/引用
指针是独立变量/指针可以改变所指地址/大小/指针有多重指针/引用必须初始化,不可为空/
引用通常用于函数传参,指针用于内存管理和访问。
4.为什么 析构函数常用虚函数 ?为什么构造函数不虚?
- 析构函数使用虚函数:可以确保资源正确释放,防止内存泄漏。在多态的情况下,基类指针可以指向派生类对象,析构函数定义为虚函数可以确保删除对象时正确调用析构函数。
- 构造函数不能定义为虚函数:构造函数的初始化用途使得构造函数不能定义为虚函数。
5.CPP中 多态的实现有哪几种 ? (静态 /动态)
- 静态多态:也叫编译时多态,主要包括运算符重载和函数重载以及模板。
- 动态多态:也叫运行时多态,包括虚函数和基类指针引用等。
6.new /malloc区别及底层实现原理
new/malloc的区别
- new不仅会分配内存还会调用对象的构造函数,而malloc无构造函数。
- new返回的是对象类型的指针。而malloc默认返回void*类型,一般要进行类型转换。
- 分配失败的处理方式不同,new会抛出异常,malloc会返回NULL。
- new是运算符可以被重载,而malloc是库函数不能被重载。
- new和malloc的底层实现原理不同(见下文)
new/malloc的底层实现
- new的底层机制主要分为两个部分:分配器和构造器。分配器负责从堆中分配内存,一般会调用第一级的内存管理函数,比如malloc或者由操作系统提供的函数。构造器用来初始化对象,包括调用构造函数,返回地址。
- malloc的底层:小型brk,大型mmap直接映射到地址空间。
7.Struct→CPP内存对齐
内存对齐方式默认由编译器决定,也可以手动设定。目的是提高内存访问速度。
- 结构体struct中每个成员变量默认都要对其它的下一位成员补齐,且最末结构体要是最大对齐因子的倍数,话不多说直接上例子。所以这个结构体占用内存为1+(3)+4+8=16,16=2*8,符合要求。
struct MyStruct {
char a; // 占用1字节,后面需要3字节的填充以对齐下一个成员
int b; // 占用4字节
double c; // 占用8字节(在大多数平台上)
};
8.进程通信
管道/共享内存/消息队列/信号/套接字