当面试官提出问题后,不要着急作答,应该适当停一下,整理一下逻辑思路。
对于简单问题的回答,尽量不要照本宣科,找准问题回答的角度/层次,争取简单问题回答的比较有亮点。
对于相对复杂的问题,比较难以阐述的问题,思考上要花一些时间,整理好逻辑思路,以及问题大致描述的顺序。
如果是现场面试(视频面试),最好用纸笔边画边讲;如果是电话面试,回答问题的过程中,需要和面试官经常沟通,不要自顾自的滔滔不绝。
对于面试中,被提问到自己不知道的内容,尽量把自己知道的相关知识都说一说,回答得饱满一些,让面试官了解我们。
你还有什么问题?
我将来在公司可能接触到的技术有哪些?
我在公司会用到什么技术,我现在掌握的C和C++去公司需不需要转型?
请面试官能不能对我的技术面试进行点评,给我提出一些宝贵的经验?
this指针来解决如何区分哪个对象调用了这个非静态的成员方法。this指针指向的就是被调用的成员函数所属的对象。this指针来区分。return *this;。如果说自定义类型,而且提供了析构函数,那么使用new []申请内存时,就一定需要用delete[]释放内存,除此之外,在用new[]申请内存时,可以用delete释放。
static修饰全局变量或者函数时,被static修饰以后,在符号表中,符号的作用域就从g变成l;static修饰局部变量时,静态局部变量存储在.data或.bss段,局部变量本身不产生符号的,通过ebp-偏移量来进行访问,而此时会产生符号,符号的作用域是l。static可以修饰成员变量,也可以修饰成员方法,不再产生this指针。既可以通过对象,也可以通过类名直接访问静态成员变量。allocator:给容器使用的,主要作用是把对象的内存开辟和对象构造分开,把对象析构和内存释放分开。这样做的目的在于:当初始化容器时,只需要给容器底层开辟空间,并不需要在空间上构造无效的对象,所以不能使用new;当从容器删除对象时,只需要把对象析构掉,并不需要释放对象的内存;当容器出作用域时,只需要把容器中有效的对象析构再释放内存,而如果使用delete,则会把容器底层所有的空间都当做对象析构掉再释放内存。map和multi_map是一个映射表,存储的元素是键值对key-value,底层的实现是红黑树。map不允许key重复,而multi_map允许key重复。C 和 C++生成符号的方式不同,C 和 C++语言之间的API接口是无法直接调用的,C 语言的函数声明必须括在extern "C"{}里面。
C++中引入了引用、函数重载、运算符重载、new\delete、const、inline、带默认值参数的函数、模版、类和对象、STL、异常、智能指针等。
malloc 称作C的库函数,而 new 被称作运算符。
new 不仅可以开辟内存空间,还可以对内存做初始化操作;而 malloc 只负责开辟内存,不负责初始化。
由于 new 在开辟内存时是指定数据类型的,所以返回值不需要进行类型的转换;而 malloc 只是按字节数开辟内存的,返回值永远是 void* 类型,因此需要对返回值进行类型的转换。
malloc 开辟内存失败,返回 nullptr 指针;而 new 开辟内存失败,是通过抛出 bad_alloc 类型的异常来判断的。
map 称为映射表,存储 [key, value] 键值队,两者的底层数据结构都是红黑树。引用计数是在堆上分配的,一个资源只能对应一个引用计数,引用同一个资源的所有智能指针是共享这个引用计数的,才不至于在增减资源引用计数时导致错乱。
迭代器不允许一边读一边修改。
哪些情况会导致容器迭代器出现失效?
erase方法后,当前位置到容器末尾元素的所有的迭代器全部失效了。insert方法后,当前位置到容器末尾元素的所有的迭代器全部失效了。insert方法并且引起了容器内存扩容,那么原来容器的所有的迭代器全部失效了。迭代器失效了以后,问题该如何解决?
对插入将删除点的迭代器进行更新操作。
通过迭代器对容器中的元素进行插入/删除操作时,成员方法insert和erase会返回一个更新后的迭代器。
set、multiset、map、multimap
STL容器分为
顺序容器:vector(底层为可扩容的数组)、list(底层为双向循环链表)、deque(底层为可动态扩容的二位数组)
容器适配器:stack(底层使用deque容器实现)、queue(底层使用deque容器实现)、priority_queue(底层是一个大根堆,使用vector容器实现)
关联式容器:有序容器set、multiset、map、multimap(底层采用红黑树实现)
无序容器unordered_set、unordered_map、unordered_multiset、unordered_multimap (底层采用链式哈希表实现)
程序编译链接需要经过以下几个阶段:
.i结尾的文件).s文件、汇编代码的优化以及生成符号表).o文件).o文件和静态库文件链接生成可执行文件,即.out文件。在链接阶段,首先会将所有.o文件段合并,符号表合并后,进行符号解析,符号解析成功后,给所有符号分配虚拟地址,然后对符号进行重定向,完成后生成可执行文件。).data段存储初始化不为0的全局变量,.bss段存储未初始化的全局变量以及初始化为0的全局变量。
堆内存的大小远远大于栈所占内存的大小
堆上的内存空间需要手动开辟和释放,而栈上的空间不需要手动开辟和释放,函数运行时,系统会自动为函数的局部变量在栈上分配空间,局部变量出作用域时,系统自动回收栈帧。这也导致堆上的资源和栈上的资源生命周期也不相同。
堆是从低地址向高地址分配堆内存,而栈是从高地址向低地址分配栈空间。
构造函数不可以是虚函数。由于构造函数完成后,才会产生对象,因此不能实现成虚函数。构造函数中调用任何函数,都是静态绑定的,即使调用虚函数,也不会发生动态绑定。
析构函数可以是虚函数。当基类的指针(引用)指向堆上new出来的派生类对象,delete pb(pb为基类的指针),它调用析构函数的时候必须发生动态绑定,否则会导致派生类的析构函数无法调用。
#define是在预编译时处理,对字符串进行替换;
而inline函数是在运行时才处理,在函数调用点,通过函数的实参把函数代码直接展开调用,节省了函数的调用开销。
内联函数有类型检查更加的安全,宏定义没有类型检查。
内联函数在运行时可调试,而宏定义不可以。
#define 可以定义常量、代码块以及函数块,而inline 只是修饰函数。
局部变量存放在栈上,通过ebp指针偏移来访问的,不产生符号。
如果使用值传递,创建对象t2时,调用拷贝构造函数,将 t1 作为实参进行传递时,首先会把t1传递给形参,此时就需要拷贝构造创建形参,当值传递时,需要再次创建新的形参,这个过程是层层循环,无穷调用,导致程序编译错误。
virtual void func() = 0;的函数就是纯虚函数。拥有纯虚函数的类称为抽象类,抽象类不能实例化对象,但是可以定义指针和引用变量。.rodata段。const 修饰的变量不能够再作为左值,初始化完成后,值不能被修改。
不能把常量的地址泄露给一个普通的指针或者普通的引用变量。
在 C++ 代码中,所有出现 const 常量名字的地方,在编译阶段就被常量初始化的值替换了。
定义const常量时,必须对其进行初始化。
const 与static 的区别:
面向过程:
const可以修饰全局变量、局部变量以及形参变量,而static只能修饰全局变量和局部变量。
const不能修饰函数,static可以修饰函数。
面向对象:
const_cast:去掉(指针或引用)常量属性的一种类型转换。
static_cast:提供编译器认为安全的类型转换。(没有任何联系的类型之间的转换就被否定了)
reinterpret_cast:类似于C风格的强制类型转换。
dynamic_cast:主要用在继承结构中,可以支持RTTI类型识别的上下转换。
#define MAP_SIZE 2、#define QUE_SIZE(T) 4096/sizeof(T)MAP_SIZE(T*),第二维数组默认开辟的大小为QUE_SIZE(T)oldsize/2开始存放定义对象的时候,使用强智能指针;引用对象的时候,使用弱智能指针。
当通过weak_ptr访问对象成员时,需要先调用weak_ptr的lock方法,把weak_ptr提升为shared_ptr强智能指针,再进行对象成员调用。
因为C++生成的函数符号依赖函数名称和参数列表,当程序编译到函数调用点时,如果函数名称和传入实参的个数和类型与某一个函数重载版本匹配,则直接调用相应的函数重载版本(静态的多态,在编译阶段处理)。
虚函数的底层实现:当一个类中含有虚函数成员方法时,在编译阶段会产生虚函数表,虚函数运行时加载到rodata段。一个类里面定义了虚函数,那么这个类定义的对象,其运行时,在内存中的开始部分,多存储了一个虚函数指针 vfptr ,指向相应类型的虚函数表 vftable,在虚函数表中获取到相应虚函数的地址 。一个类型定义的多个对象,它们的虚函数指针 vfptr 指向的是同一张虚函数表。
AVL树是一种自平衡二叉查找树,它的每个节点都有一个平衡因子,当某个节点的平衡因子小于1时,需要进行旋转操作来保持平衡。而红黑树是一种特殊的二叉查找树,它通过保持节点的红色或黑色来保持平衡。
红黑树不要求绝对平衡,需要对节点着色;而AVL树要求绝对平衡,需要通过平衡因子计数,旋转次数可能会比红黑树多。数据量比较大的情况下,使用红黑树的效率更高。
map的底层是通过红黑树实现的,每个数据存储为键值对,默认是对key进行小于<的比较操作,因此,当map的键是类类型时,需要提供operator<()运算符重载函数。
设计成SGI STL二级空间配置器的内存池的实现就可以了。然后展开说一下具体的实现细节。