C++最常见面试问题(三)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

一、堆栈的区别

(1)堆栈空间分配区别:
栈(操作系统):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈;
堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式类似于链表。
(2)堆栈的缓存方式区别
栈:是内存中存储值类型的,大小为2M(window,linux下默认为8M,可以更改),超出则会报错,内存溢出。
堆:内存中,存储的是引用数据类型,引用数据类型无法确定大小,堆实际上是一个在内存中使用到内存中零散空间的链表结构的存储空间,堆的大小由引用类型的大小直接决定,引用类型的大小的变化直接影响到堆的变化。
(3)堆栈数据结构上的区别
堆(数据结构):堆可以被看成是一棵树,如:堆排序;
栈(数据结构):一种先进后出的数据结构。

二、c++中struct和class的区别

C++中 struct 与 class 的区别:

(1)内部成员变量及成员函数的默认访问属性:struct 默认防控属性是 public 的,而 class 默认的访问属性是private的。
(2)继承关系中默认访问属性的区别:在继承关系,struct 默认是 public 的,而 class 是 private.
(3)class这个关键字还可用于定义模板参数,就等同于typename;而strcut不用与定义模板参数。

三、频繁对vector调用push_back()对性能的影响和原因?

在一个vector的尾部之外的任何位置添加元素,都需要重新移动元素。而且,向一个vector添加元素可能引起整个对象存储空间的重新分配。重新分配一个对象的存储空间需要分配新的内存,并将元素从旧的空间移到新的空间。

四、简述C++异常处理方式

一般情况下C++异常处理包含以下几个步骤:
(1)程序执行时发生错误;
(2)以一个异常对象(最简单是一个整数)记录错误的原因及相关信息;
(3)程序监测到这个错误(读取异常对象);
(4)程序决定如何处理错误;
(5)进行错误处理,并在此后恢复/终止程序的执行。

五、程序大量molloc和free会有什么后果,怎么解决

造成的后果:
malloc和free大量使用后回造成内存碎片,内存碎片一般是由于空闲的连续空间比要申请的空间小,导致这些小内存块不能被利用,造成资源浪费。
举例说明:
假设有一块一共有100个单位的连续空闲内存空间,范围是0-99,如果你从中申请一块内存,如10个单位,那么申请出来的内存块就为0-9区间。这时继续申请一块内存,比如说是5个单位大小,第二块得到的内存块就应该为10-14区间。如果把第一块内存块释放,然后再申请一块大于10个单位的内存块,比如说20个单位。因为刚被释放的内存块不能满足新的请求,所以只能从15开始分配出20个单位的内存块。现在整个内存空间的状态是0-9空闲,10-14被占用,15-24被占用,25-99空闲。其中0-9就是一个内存碎片了。如果10-14一直被占用,而以后申请的空间都大于10个单位,那么0-9就永远用不上了,造成内存浪费。

解决方法:
使用高性能之内存池,内存池(Memory Pool)是一种内存分配方式。通常我们习惯直接使用new、malloc等API申请分配内存,这样做的缺点在于:由于所申请内存块的大小不定,当频繁使用时会造成大量的内存碎片并进而降低性能。内存池则是在真正使用内存之前,先申请分配一定数量的、大小相等(一般情况下)的内存块留作备用。当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存。这样做的一个显著优点是尽量避免了内存碎片,使得内存分配效率得到提升。
(1)针对特殊情况,例如需要频繁分配释放固定大小的内存对象时,不需要复杂的分配算法和多线程保护。也不需要维护内存空闲表的额外开销,从而获得较高的性能。
(2)由于开辟一定数量的连续内存空间作为内存池块,因而一定程度上提高了程序局部性,提升了程序性能。
(3)比较容易控制页边界对齐和内存字节对齐,没有内存碎片的问题。
(4)当需要分配管理的内存在100M一下的时候,采用内存池会节省大量的时间,否则会耗费更多的时间。
(5)内存池可以防止更多的内存碎片的产生。
(6)更方便于管理内存。

六、C++源码到可执行程序的过程

对于C++编写的程序,从源代码到可执行文件,一般经过下面四个步骤:
1).预处理,产生.ii文件。
预处理主要包含下面的内容:
a.对所有的“#define”进行宏展开;
b.处理所有的条件编译指令,比如“#if”,“#ifdef”,“#elif”,“#else”,“#endif”
c.处理“#include”指令,这个过程是递归的,也就是说被包含的文件可能还包含其他文件
d.删除所有的注释“//”和“/**/”
e.添加行号和文件标识
f.保留所有的“#pragma”编译器指令
经过预处理后的.ii文件不包含任何宏定义,因为所有的宏已经被展开,并且包含的文件也已经被插入到.ii文件中。
2).编译,产生汇编文件(.s文件)。
编译的过程就是将预处理完的文件进行一系列词法分析,语法分析,语义分析及优化后生成相应的汇编代码文件(.s文件)。
3).汇编,产生目标文件(.o或.obj文件)。
汇编器是将汇编代码转变成机器可以执行的代码,每一个汇编语句几乎都对应一条机器指令。最终产生目标文件(.o或.obj文件)。
4).链接,产生可执行文件(.out或.exe文件)。
链接的过程主要包括了地址和空间分配(Address and Storage Allocation)、符号决议(Symbol Resolution)和重定位(Relocation)

七、变量的声明和定义有什么区别?

为变量分配地址和存储空间的称为定义,不分配地址的称为声明。一个变量可以在多个地方声明,但是只在一个地方定义。加入 extern 修饰的是变量的声明,说明此变量将在文件以外或在文件后面部分
定义。

注意:很多时候一个变量,只是声明不分配内存空间,直到具体使用时才初始化,分配内存空间,如外部变量。

八、C 语言的关键字 static 和 C++ 的关键字 static 有什么区别?

在 C 中 static 用来修饰局部静态变量和外部静态变量、函数。而C++中除了上述功能外,还用来定义类的成员变量和函数。即静态成员和静态成员函数。

注意:编程时 static 的记忆性,和全局性的特点可以让在不同时期调用的函数进行通信,传递信息,而 C++的静态成员则可以在多个对象实例间进行通信,传递信息。

九、野指针与悬空指针有什么区别?如何避免?

区别:
(1)野指针(wild pointer):就是没有被初始化过的指针。⽤ gcc -Wall 编译, 会出现 useduninitialized 警告。
(2)悬空指针:是指针最初指向的内存已经被释放了的⼀种指针。
⽆论是野指针还是悬空指针,都是指向⽆效内存区域(这⾥的⽆效指的是"不安全不可控")的指针。 访问"不安全可控"(invalid)的内存区域将导致"Undefined Behavior"。
如何避免:
在平时的编码中,养成在定义指针后且在使⽤之前完成初始化的习惯或者使⽤智能指针。

十、成员函数和友元函数的区别

(1)成员函数是类定义的一部分,通过特定的对象来调用。成员函数既可以隐式访问调用对象的成员,而无须使用成员操作符;
(2)友元函数不是类的组成部分,因此被称为直接函数调用。友元函数不能隐式访问类成员,而必须将成员操作符用于作为参数传递的对象。

猜你喜欢

转载自blog.csdn.net/qq_46901210/article/details/124218282