2020字节跳动一面面经

1. vector扩容机制

C++中,vector的元素是顺序存放在内存中的,当vector申请的空间被元素占满之后,需要申请一块新的空的更大的内存,如果每次用增加一个元素的大小的方式申请内存,cpu的开销是比较大的。为了解决这个问题,c++中申请新内存的大小要比原来大很多,而不是只大一个元素的大小,将旧地址上的元素复制到新地址上面来。通常来说,gcc中vector用两倍的方式扩容。扩容之后,因为vector挪到新的内存空间,所以原来的迭代器就会失效。除此之外,插入,增加,复制都会引起vector的扩容。

2. 构造函数中不给变量赋值初始化为什么值

当类对象为全局变量的时候,系统加载的时候,初始值为0。当类对象为局部变量的时候,在栈上分配空间,成员变量值不确定。函数被调用时,栈会分配一部分空间存放该函数中的局部变量(包括参数),这片新分配的存储空间中原来的内容是什么,局部变量的初始内容也就是什么,因此局部变量的初始值是不可预测的。

3. 构造函数和对象的初始化

构造函数不是为了给对象分配内存空间,而是为了初始化已经分配的内存空间。

4. c++内存空间分配

c++内存分为5个区,堆,栈,自由存储区,全局存储区,常量存储区。

栈:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

堆:就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。

代码存储区:存储代码

全局存储区:存储全局变量和静态变量,会被初始化。

常量存储区:存放常量,不能被修改。

堆和栈的区别:

管理方式 编译器管理 程序员手动
空间大小 空间较大 空间较小
碎片问题 new/delete会造成空间不连续 先进后出,不产生碎片
生长方向 向上生长 向下生长
分配方式 动态分配 静态分配+动态分配
分配效率 效率低 效率高
5. 对象的内存机制
  1. 静态建立对象:如果直接用A a声明一个对象,由编译器为对象在空间中分配内存,是通过直接移动栈顶指针,挪出适当的空间,然后(编译器)在这片内存空间上调用构造函数形成一个栈对象。使用这种方法,直接调用类的构造函数。当使用完了后,编译器自动调用析构函数对这片栈内存进行释放。
  2. 动态建立对象:使用new运算符将对象建立在空间中。这个过程分为两步,第一步是(编译器)执行operator new()函数,在堆空间中搜索合适的内存并进行分配;第二步是调用构造函数构造对象,初始化这片内存空间。这种方法,间接调用类的构造函数。当对象使用完毕后delete也会调用析构函数对堆上的内存进行释放
  3. 非静态类成员变量:放在对象的内存空间中
  4. 静态类成员变量:放在全局常量存储区
  5. 普通成员函数:看作类作用域的全局函数,存储在代码区。
  6. 虚函数:存储虚函数指针(4字节),在对象的内存空间中。
  7. 虚函数表:虚函数表在编译的时候就能确定好,所以存储在静态存储区,类似于类中的static成员。
6. 类大小的计算:
  1. 空类:空类的sizeof是1,编译器会给空类增加一个字节,这样实例化之后,类可以具有独一无二的地址。
  2. 只含有虚函数的类:虚函数指针大小是4字节,所以对象的大小是4字节。
  3. 虚继承:虚基类指针的大小是4字节。
  4. 含有数据成员的类字节对齐
7. new和malloc的区别
new malloc
返回类型 对象类型指针 void* ,强制转换
分配失败 bac_alloc 异常 NULL
参数 不需要指定大小 指定大小
内存区域 自由存储区
内存扩充 无法扩充 使用realloc
重载 可以重载 不支持重载
8. c++文件运行过程(静态链接和动态链接)

静态链接:把有关系的文件链接起来形成静态库,以目标文件为单位进行连接。

扫描二维码关注公众号,回复: 10961209 查看本文章

动态链接:动态链接的基本思想是把程序按照模块拆分成各个相对独立部分,在程序运行时才将它们链接在一起形成一个完整的程序,而不是像静态链接一样把所有程序模块都链接成一个单独的可执行文件。 省空间,但是在运行的时候进行链接很慢。

猜你喜欢

转载自www.cnblogs.com/hang-shao/p/12734782.html