面试宝典
基础知识点
-
规则: 1、每个变量所对应的偏移量都必须是该变量本身所占用字节数的整数倍。 2、该结构体整体占用内存大小必须是结构体中占用最大内存变量的字节数的整数倍 可以使用#pragma pack(x)来指定按 x 字节的整数倍来对齐。
-
四种cast操作符:C++四种cast操作符、C++中static_cast和dynamic_cast强制类型转换、dynamic_cast转换类指针时,基类需要虚函数
关于dynamic_cast和虚函数的关系的例子见此博客。
dynamic_cast转换错误会返回什么? 答:NULL
const_cast:博客
const_cast要通过指针使用。 const_cast转换之后的数据依然是常量,不能通过赋值修改。 之所以使用const_cast,是因为我们可能调用了一个参数不是const的函数,而我们要传进去的实际参数确实是const的,于是我们就需要使用const_cast去除const限定,以便函数能够接受这个实际参数。
-
右值引用:右值引用与移动构造函数(重点看)、博客1、博客2、c++ 之 std::move
-
引用:引用和指针的区别
引用和指针做函数参数的区别? sizeof(指针)和sizeof(引用)的区别? 指针能否为空,引用能否为空?
-
Lambda表达式:博客
-
内存管理:堆、栈、自由存储区(抽象概念)、全局/静态存储区、常量存储区、代码区
-
new与delete的工作步骤:
使用new操作符来分配对象内存时会经历三个步骤: 第一步:调用operator new 函数(对于数组是operator new[])分配一块足够大的,原始的,未命名的内存空间以便存储特定类型的对象。 第二步:编译器运行相应的构造函数以构造对象,并为其传入初值。 第三步:对象构造完成后,返回一个指向该对象的指针。 使用delete操作符来释放对象内存时会经历两个步骤: 第一步:调用对象的析构函数。 第二步:编译器调用operator delete(或operator delete[])函数释放内存空间。
-
new与malloc的区别:博客
-
string底层:博客
-
C++11:十大必掌握C++11新特性
-
brk()的参数设置为新的heap上界地址,成功返回1,失败返回0; sbrk()的参数为申请内存的大小,返回heap新的上界的地址; mmap()向映射区申请一块内存。 malloc采用的是内存池的管理方式,使用空闲链表管理内存块。 首先在空闲链表寻找内存块进行分配,不行就向heap申请, heap不够就判断需要分配的空间大小,若小于mmap分配阈值(128kb)就调用sbrk来扩大heap,若大于阈值则调用mmap向映射区申请内存。
-
c++读写文本文件和二进制文件:read()和write()、>>和<<、文本文件以及二进制文件的读写
read()和write()用于以二进制方式读写,运算符>>和<<以文本方式读写
-
用程序判断大端、小端(记住联合体的方法)
-
i++在两个线程里边分别执行100次,能得到的最大值和最小值?博客1、博客2
i=0,i++在两个线程分别执行100次 -> [2,200] j=100,两个线程j–-,均执行50次 -> [0,98]
封装、继承、多态
-
虚基表指针记录虚基表的地址,虚基表记录公共基类成员相对于虚基表指针地址的偏移量, 通过偏移量可以找到公共基类成员的位置
-
C++静态多态与动态多态 (重要!!)
-
C++多态(虚函数、静态链接、动态链接、虚函数表、虚表指针)
虚函数的调用关系:this->vptr->虚函数表->虚函数
-
子类对象的vptr指针指向子类虚函数表,但是在调用父类的构造函数的时候创建,可以认为Vptr指针是属于父类的成员,但在调用子类的构造函数时Vptr又被赋值指向子类的虚函数表。
-
虚函数表位于只读数据段(.rodata),也就是C++内存模型中的常量区;而虚函数则位于代码段(.text),也就是C++内存模型中的代码区:C++中的虚函数表和虚函数在内存中的位置
-
虚函数返回值协变: c++返回类型协变
-
重载运算符:代码
-
重载赋值运算符时有指针变量:先释放原本指向的堆内存空间,再进行深拷贝。
/* 对象不存在,且没用别的对象来初始化,就是调用了构造函数; 对象不存在,且用别的对象来初始化,就是拷贝构造函数; 对象存在,用别的对象来给它赋值,就是赋值函数。 */ class A; A a; A b=a; //调用拷贝构造函数(b不存在) A c(a) ; //调用拷贝构造函数 /****/ class A; A a; A b; b = a ; //调用赋值函数(b存在)</span>
-
哪些函数不能设置为虚函数:博客1、 C++中构造函数不能声明为虚函数
构造函数不能为虚函数的原因: 假设构造函数为虚函数,虚函数执行依赖于虚函数表,找到虚函数表要通过对象中的vptr指针, 然而此时该对象构造函数还未执行,vptr指针还没能完成初始化,找不到虚函数表,因此二者矛盾。 静态成员函数不能为虚函数的原因: 静态成员函数没有this指针,虚函数的实现是为每一个对象分配一个vptr指针指向虚函数表,而vptr是通过this指针调用的,所以不能为virtual。
-
将类定义为抽象基类或者将构造函数声明为private。 那构造函数为private时要怎么在类内构造对象? 答:在静态成员函数中创建对象,或者利用友元函数实现。
STL
-
优先队列priority_queue:博客
-
仿函数:greater()、less()
-
vector调用push_back如果发生扩容:
关于c++中vector的push_back、拷贝构造和移动构造
当vector容量不足扩容时对旧元素调用的是移动构造函数,对于新加入的元素调用的是拷贝构造函数或者移动构造函数; 那么对于有n个元素的vector调用push_back,若不涉及扩容,则调用一次拷贝构造函数或者移动构造函数;若涉及扩容,则调用n次移动构造函数和一次拷贝构造函数或者移动构造函数。 因此当vector存储自定义对象的时候,如果有指针,记得要在自定义类中补充拷贝构造函数(保证深拷贝)和移动构造函数(扩容时直接转移所有权,不需要拷贝,高效)。
-
倍增因子为m,直接记住时间复杂度为O(m/(m-1))即可!!! 2倍扩容为O(2),1.5倍扩容为O(3) 。 假设N个数,则有:2^0, 2^1, 2^2, ..., 2^log(N-1) 总时间复杂度=2(1-2^logN)/(1-2)= 2^(logN+1)-2=2N-2 平均时间复杂度=(2N-2)/N=2*(N-1)/N=2 因此两倍扩容时,时间复杂度为O(2)。