C++之面试高频50问(速通面试)

问题来源:小黛的求职笔记(妥妥的大佬)
下面有50个左右关于C++的面试高频问题,保证你看完熟悉后,能应付基本的C++的问题啦,至于进阶拔高的C++面试问题后续我再整理,发出来,可以关注下哦。
1.说说C++和golang、Python、java、C等语言的区别?
我觉得要从其语言特点和适用场景来分析,像C语言和c++语言,他们之间的关系更像父子关系,一方面c++继承了C语言的强大功能,一方面又增加了类,多态,继承这些面向对象的编程特性,所以更适合游戏,图形处理,操作系统这些领域,而C适合系统开发,嵌入式领域。
那go语言的特点是轻量级,高并发,所以特别合适网络编程和大规模分布式系统。
那Python语言的特点是易学易读,可移植性高,适合数据分析,以及机器学习的场景领域。
那Java语言的特点是跨平台性高,可靠性高,适合大型企业应用程序的开发。
那我们可以根据所面临的场景需要,选择不同的语言,方可事半功倍。

2.如何理解C++面向对象编程,说下面向对象与面向过程的区别?
面试官,我举个例子去说明面向对象和过程的区别?
假设我们现在需要去模拟一个银行账号的操作,如果是面向过程,那么我们会写一系列的函数比如,创建账号函数,存款函数,取款函数;而如果是面向对象,那么会把银行账号抽象成一个类,在个类里提供其的属性,其的创建账号函数,存款函数,取款函数。相同是大家都能解决问题,但不同的是下次如果我们要创建一个VIP账号,面向过程的代码要全部推倒重写,而面向对象只需要使用继承和多态进行改写即可。
所以面向过程和面向对象的本质区别是,前者更强调程序的顺序和流程,后者强调模块化,对象化,使得我们程序更容易编写和维护。PS:面向过程也有自己适合的场景,比如一些简单的场景,用其更加简洁和直观,避免了过度设计和冗余的代码。

3.如果理解C++中的封装,继承,多态?
其是c++的三大特性!
封装就是把属性和方法通过公有和私有还有保护的方法封装起来,一般来讲,把函数封装成公有供外部调用,把成员变量封装成私有的,外部不能调用,此外,还可以用protect保护,供派生类调用。
继承就是从一个类派生出其子类,在这过程中,子类可以加入属于自己的属性和方法。
多态就是同一个方法,不同的对象去调用,会不同的结果,其是由虚函数实现的
这三大特性都使得我们的代码程序变得更灵活和健壮了。

4.编译时多态,运行时多态有什么区别?
(不要被迷惑住了,其实就是问静态多态和动态多态的区别)
多态分为两种,
一种是静态多态,在编译时便确定下来,常见由函数重载,模板实现。优点是编译时完成,效率高!强大:比如STL就是由模板的泛型设计的产物;缺点是调试困难。
一种是动态多态,在运行时才完成的,其是由虚函数实现的,不同的子类重写父类某个方法,最终达到多态的效果。优点是可以继承原有的代码,可复用;缺点是运行时才完成,运行会有点慢,修改接口或者修改某个类会导致整个类体系的问题。

5.什么是静态成员,类的静态成员,如何赋值,如何使用?
用static修饰的成员叫静态成员
类的静态成员不独属于任何一个对象,在类实例化之前,这个静态成员便存在了
赋值需要在类外进行初始化 …如 int MyClass::count = 0;
直接用类名加::访问 …如MyClass::count = 1;

6.继承中,构造函数的执行顺序,析构函数的执行顺序是怎么的?
当我们构造一个派生类时,会先找到基类的构造函数,再逐层向下构造,直到调用我们要创建的这个派生类的构造函数。
而当我们析构一个类时,就直接先析构这个类,完成后再去调用父类的析构函数,直至基类的析构函数被调用。

7.如何判断一个实例是属于哪个类?
三种方法
第一种,使用typeid运算符来判断,因为这个运算符可以返回一个对象的类型信息,包括类型名称和类型标识符,可以用这个来判断是哪个类
第二种,用虚函数,如果一个实例属于这个类,那么它一定是可以调用这个类的虚函数,如果不能调用则说明不是这个类。
第三种,用dynamic_cast运算符,这个运算符的作用是将父类的指针或引用转换成子类的指针或引用,那我们把尝试把实例的指针用dynamic转换,看其能不能转化成派生类的指针,如果能就会返回了具体的指针,不能就会返回一个空指针,则说明该实例不属于这个类,换下一个判断。
第一种比较高效,第二三种开销大点,但安全可靠点。

8.如何避免外部实例化这个类的对象?
第一种方法把构造函数私有化
第二种方法把这个类变成抽象类
第三种方法使用C++11里的delete,表明这个函数不能被调用。

9.C++一个空类占多少个字节?为什么?
1字节。在c++中为了确保每一个类有唯一的标识符,必须给这个类,即使是空类,也要开辟唯一的内存空间。

10.什么是空白基类最优化?
先解释下空白基类会有啥问题:
空白基类是指没有成员变量,也没有成员函数。
那么此时有很多类继承了空白基类,那么每当实例化一个派生类,就会生成一个空白类的实例,就会造成空间的浪费。
所以需要进行空白基类最优化,将空白类的实例和派生类的实例合并,避免重复的存储,具体来讲,就是编译器可以控制成员变量的偏移量,将空白类的实例放置到派生类的尾部,这样一来就避免了,空间的浪费。
这种技术只能适用于空白基类。

11.C++一个空类默认有哪几个函数,请写出函数定义?
有四个默认成员函数,分别是下面几个

class EmptyClass
{
	EmptyClass(); //默认构造函数
	~EmptyClass();//默认析构函数
	EmptyClass(const EmptyClass& other) {};//默认拷贝构造函数
	EmptyClass operator=(const EmptyClass& other){return *this};
	//默认拷贝赋值函数
};

12.介绍下内存分区?
内核空间:存储操作系统的信息,例如进程表。
栈区:存储局部变量,函数参数,返回地址这些,自动申请释放。
共享区:多个进程共享其空间,也可以进行进程间的通信。
堆区:动态内存,由malloc,new这些申请,需要手动调用,free,delete释放空间。
全局区/静态区:储存全局变量和静态变量。
常量区:存储常量,只能读,不能修改。
代码区:存储代码,只能执行,不能修改。

13.堆区和栈区的区别?
第一点 栈的空间是系统申请释放 堆是手动
第二点 栈的空间小 只有几MB 而32位的Linux最多有2G
第三点 栈的申请空间释放更高效 堆的申请释放较慢,而且还会有内存碎片的问题
第四点 栈是线性的空间 堆类似于把一块块内存挂在链表上
第五点 栈是从高地址到低地址 堆是低地址到高地址。

14.什么是内存碎片
当我们使用new 或者malloc去申请内存时,由于堆空间是链表挂着内存的,我们东一块,西一块的取,大一块,小一块的取,就很可能会造成内存的碎片,使得堆空间“支离破碎”,很难再拿出一个整块的大内存。

15.智能指针都有哪几种?
四种啊!
第一种auto_ptr 通过转移资源所以权来解决复制问题
第二种unique_ptr 通过将构造函数,拷贝构造,拷贝赋值函数私有化的方式,解决复制问题。
第三种 shared_ptr 引用计数
第四种 weak_ptr 弱引用解决第三种智能指针的循环引用问题。

16.shared_ptr是线程安全的吗?
是的,通过原子操作和引用计数保障了其是线程安全的,但这不代表它指向的对象是线程安全的,还是要虚怀若谷,认真分析有无线程安全问题。

17强制类型的转换方法?
四种,
static_cast
reinterpret_cast
cosnt_cast
dynamic_cast。

18.列举下c++中的异常,构造函数中可以有异常吗,析构函数中可以有异常吗?
标准异常,运行时异常,逻辑异常,STL异常
可以,但不建议,因为当构造出现异常,说明初始化没有完成,导致对象不能使用的问题
可以,但不建议,要是析构中出现异常是在清理内存之前,那么就会造成内存的泄露。

19.介绍下什么时候会发生栈溢出和堆溢出?
当我们的函数嵌套过深,或者递归过深会导致栈溢出
当我们申请的动态内存大于堆的剩余大小,或者我们试图使用已经被释放的内存空间,此时会导致堆溢出。
解决办法:程序员别作死,使用内存泄漏检测工具,编译警告。

20.函数传参的方式以及区别?
有以下几种
第一种值传递: 拷贝一份实参给形参,形参不影响实参,效率低
第二种指针传递: 把实参的地址传递给形参,通过指针访问
第三种引用传递: 直接引用实参,可以修改,可以读取,效率高
第四种const引用传递:和引用传递一个效果,但不能修改实参。

21.介绍构造函数和析构函数?
分为默认的和自己设置的,对于内置类型,比如int char可以直接使用默认构造和析构函数完成即可,对于自定义类型最好我们自己去定义构造和析构函数。

22.父类的析构函数要设置成虚函数吗?为什么?
最好设置成析构函数。
因为当一个基类指针指向派生类对象时,进行析构,如果父类没有把析构函数定义成虚函数,那只会调用父类的析构函数,而不会调用子类的析构函数,造成内存泄漏。
但如果是派生类指针指向派生类对象,则不需要定义成虚函数。

23.多态是怎么实现的?
每个类有对应的续表指针,当父类指针指向父类时,找虚表指针,找到虚表,拿出父类所需的虚函数,同理,当父类指针指向子类时,找到子类的虚表指针,找到子类的虚表,拿出子类所需的虚函数,两个不同的函数,调用的结果就不同,自然而然就形成了多态。

24什么是菱形继承,如何解决?
A是基类,B,C继承A,此时D同时继承了B和C,此时便是菱形继承,会出现访问二义性和内存浪费的问题。解决办法,使用虚继承,B和C使用virtual继承A,这样一来,派生类只有存在一个虚基类实例,解决了访问二义性和内存浪费的问题 。

25.this指针是什么,有什么用,struct里面有this指针吗?
this指针是指向当前对象的,每个对象都有一个this指针,非静态成员函数不能使用this指针,可以通过它来访问成员变量和函数,struct有this指针。

26.this指针存储在哪?
this指针是指向对象的,它既不在对象的空间里,也不在栈和堆区。
当调用成员函数时,编译器会自己把this传参给函数,所以this指针存储在寄存器中。

27.struct和class最本质的区别?
在于默认的访问控制,struct是公共的,而class是私有的。
继承的话,也是一样。

28.typedef和define的区别?
前者只能创建类型别名,后除了创建类型别名,还能创建常量宏,函数宏。
前者是C++关键字,后者是C++预处理器指令。

29.const和define的区别?什么时候const无法代替define?
前者是C++关键字,后者是C++预处理器指令。
前者用来定义常量,且该常量可以进行类型检测,而后者定义的常量没有类型,不能进行类型检测
当我们需要定义字符串常量和字符数组的时候,必须使用define来定义。

30.const int* a和int const* a以及int* const a区别是什么?
第一种 指针可以修改 所指向的值不能修改
第二种 指针可以修改 所指向的值不能修改
第三种 指针不能修改 所指向的值能修改。

31.独占性智能指针如何赋值?
我还以为是啥呢? 就是unique_ptr,使用move函数将源指针的所有权转移到目标指针上,完成后,源指针变成空指针,目标指针指向对象。

32.lambda表达式如何接受外部传入的参数?
可以值传递,也可以引用传递,也可以捕获变量来接受外部的参数。

33.介绍你掌握的C++11特性?
很多啊
auto自动推导类型
范围for遍历
强制类型转换
智能指针
右值引用转发等等

34.介绍下右值引用?
右值引用的主要作用是实现移动语义和完美转发。在移动语义中,右值引用可以用于将对象的资源所有权从一个对象转移到另一个对象,以提高程序的效率。在完美转发中,右值引用可以用于将函数参数的类型完全保留,以便将其转发到另一个函数,同时保持最大的效率和安全性

35.C++中访问map的value的方法?
两种
第一种【】,如果不存在,则自动插入一个键值对
第二种find函数,找到返回具体位置的迭代器,没有就返回指向map尾部的迭代器。

36.迭代器中begin()和cbegin()的区别?
都是返回第一个元素的迭代器,begin()返回的迭代器可以修改元素的值,而cbegin()返回的是const属性的,不能通过这个迭代器去修改元素的值

37.vector中的resize和reserve有什么区别?
resize既修改size大小,也修改capacity大小,新的大小小于原先的大小,则抛弃尾部的元素。
而reserve只修改容量,即只修改capacity大小。

38.vector如何清空所占内存,如何清空size,如何清空capacity?
调用clear函数清空size设置为0
调用shrink_to_fit函数将capacity也设置成0。

39.vector扩容机制是怎么样的?
要是当前容器大小快超过容器容量时,按着之前设定好的扩容大小,比如2倍,然后把原有的元素挨个复制到新的空间,并把原先的空间释放了,把指针指向新空间的首地址。同时要注意,因为空间已经变了,原先的迭代器不能再用了,需要重新获取迭代器。

40.删除元素迭代器失效,对于不同类型的容器是怎么样的?
vector里的迭代器某个元素被删除后,后面所有的元素前移一位,后面的迭代器全失效,前面的仍有效。
list删除某个元素,只有被删除的元素的迭代器失效,其他还有效。
map和set,删除某个元素,与该元素相关的元素的迭代器都失效。

41.vector和list的区别?
第一,vector底层是一段连续的线性空间,而list底层是双向链表。
第二,vector扩容需要动态,之前的空间被释放,而list直接在尾部挂节点即可
第三,vector更适合查找的场景,list更适合删除和插入的场景。

42.map和set的区别?
基本都一样,红黑树实现,有序。
本质的区别在于,map是k-v模型,而set是k模型。

43.map中【】和insert的区别?
【】是取值的啊,当用【】访问一个不存在的元素,则进行默认插入,不需要传什么东西;而insert插入不存在的元素,需要带上键值对pair对象。

44.红黑树查找复杂度最差,平均是多少?
最差O(n),平均O(logn)

45.C++多线程如何实现,调用的哪个库?
调用的thread线程库。

46.多线程如何保证线程安全?
我知道有以下几种
第一种互斥量的使用,也就是加锁。
第二种条件变量的使用,条件满足后,再唤醒其他线程进程操作
第三种原子操作 保证了共享资源的安全
第四种使用线程局部存储,给每个线程分配内存,这个内存仅该线程可见。

47.设计一个接口需要注意什么,什么是一个好的接口?
一个好的接口应该是这样的,第一呢,稳定性,兼容性强;第二呢,可扩展,使得代码可复用;第三呢,易读性强,要给用的人用着舒服。
还有安全性等等。其次,最好给出这个接口的文档说明,怎么使用,主要内容是什么,要注意什么问题?方便他人,就是方便自己,你品,你细品。

48.代码由源代码到可执行文件的转化过程?
预编译–头文件展开,替换宏
编译-----将代码转化成汇编语言
汇编-----将汇编转化成二进制文件
链接-----将二进制文件链接上,便可以运行了。

49.动态链接和静态链接的区别?
动态链接无需将库加入到可执行文件中去,节省空间
静态链接将具体的库加入到可执行文件中去,无需依赖库,但增加了大小。

50.大端,小端?网络字节序,主机字节序?
大端是指高位字节被存储到低地址,而低位字节被存储到高地址。
小端是指高位字节被存储到高地址,而低位字节被存储到低地址。
为了通信时传输信息的正确性和可靠性,需要统一数据格式,所以有了网络字节序,要求计算机在发送数据时,统统转化成大端。而主机字节序就是计算机内的存储方式,有大端,也有小端。

呼!终于整理完了,但是好像还是缺了点什么 嘻嘻!
那肯定是压轴的指针和引用有什么区别啦! 这个就交给大家,冲冲冲!

你可能感兴趣的:(新希望,面试,c++)