1、STL中的迭代器失效的问题?
- 迭代器是不允许一边读一遍修改的(通过迭代器来遍历容器)
- 当通过迭代器插入一个元素,所有迭代器就都失效了
- 当通过迭代器删除一个元素,当前删除位置到后面所有的元素的迭代器就都失效了
- 当通过迭代器更新容器元素以后,要及时对迭代器进行更新, insert / erase方法都会返回新位置的迭代器。
2、STL中哪些底层由红黑树实现?
- set
- multiset
- map
- multimap
3、struct和class的区别?
- 定义类的时候的区别:struct定义的类默认是public的,class定义的类不写访问限定符是默认private的。
- 继承时,派生类 class B : A 默认私有继承;struct B : A 默认是公有继承
- struct 空结构体是0;struct 空类是1
- 对于C++11中 struct Data { int ma, int mb}
Data data = {10, 20};//可以这样进行初始化 - class在template< class T >还可以定义模板类型参数
4、vector和数组的区别,STL的容器分类,各容器底层实现?
直接使用数组, 数组的内存够不够用,是不是需要扩容?需要扩容代码怎么写?会和业务代码混杂一块。
vector是数组的一个面向对象的表示,把数组封装起来了。自动扩容,添加,删除,不用担心数组的内存和空间越界。
vector: 可扩容的数组
deque:动态开辟第二维的数组
list :双向循环链表
stack(依赖deque适配):容器适配器,没有自己的数据结构和迭代器
push pop queue(依赖deque的方法)
priority_queue(优先级队列,大根堆,基于vector的)
set/map(红黑树)
unordered_set/unordered_map :(链式哈希表)
5、编译链接全过程?
预编译、编译、汇编 =》 二进制可重定向obj文件 *.o
链接: 1、合并段 ,符号解析; 2、符号的重定向 =》 可执行文件
6、初始化全局变量和未初始化全局变量有什么区别?
.data(初始化,且初始值不为0) .bss(未初始化,初始化为0)
7、堆和栈的区别?
- 堆内存的大小远远大于栈内存
- 但是堆申请空间要malloc/new free/delete 手动的free,delete,否则内存泄漏;
- 栈内存: 函数的运行需要在栈上分配栈帧,函数的局部变量在栈上定义,函数出大括号运行完了之后系统自动回收函数的栈帧。
堆的分配是从低地址 =》 高地址
栈的分配是从高地址 =》 低地址
8、构造函数和析构函数可不可以是虚函数,为什么?
构造函数不能是虚函数:
- 因为虚函数必须把函数地址放在虚表中,虚表是通过虚函数指针访问的,虚函数指针在对象里面,虚函数就是基类指针指向派生类对象,调用的是派生类的同名覆盖方法,当派生类对象构造的时候,要先构造基类,在基类调用基类的构造函数,基类的构造函数是虚函数,就调用派生类的构造函数了吗???不能!!!如果能,派生类从基类继承而来的成员就不能初始化了!!
- 而且构造函数在调用的时候对象还不存在!!!对象不存在,哪来的虚函数指针???没有虚函数指针怎么访问虚函数表???
析构函数可以是虚函数:
- 基类指针指向堆上的派生类对象的时候,如果基类的析构函数是普通函数,delete p,p只是基类指针,最终只调用基类的析构函数,派生类的析构函数调用不了。所以,我们把基类的析构函数实现成虚析构函数。
Base *p = new Derive(); 把基类的析构函数实现成虚析构函数
delete p; // 对析构函数的调用进行动态绑定 ~Base() ~Derive()都可以调用
动态绑定就是访问p指向的对象是Derive类型,就访问Derive的虚函数表,在Derive的虚函数表上放的是派生类的析构函数,所以派生类和基类的析构函数就可以调用到。
9、构造函数和析构函数中能不能抛出异常,为什么?
构造函数不能抛异常:
- 如果构造函数抛异常,说明对象创建失败,编译器就不会调用对象的析构函数了
析构函数不能抛异常:
- 如果抛异常,后面的代码就无法得到执行了(资源泄漏),在最后一行抛异常就可以。
解决方法:
- 把分配的堆内存用智能指针来代替;
- 在构造函数写简单的,然后把可能抛异常的代码封装在init函数中,init() 保证对象创建是成功的!