2019 社招腾讯后台开发总结三 编程语言篇

面试题型来源于网上收集的面经,答案仅供参考,如果错误,及时指正。

1. RAII 机制

         RAII即资源请求即初始化,是C++的一种编程技术,将资源的生命周期和对象的生命周期绑定起来,在对象的构造函数里创建资源,在对象的析构函数里释放资源。

2. Sizeof 用法以及在哪一个阶段确定

         Sizeof用来计算变量或者表达式的返回值大小,表达式不会被计算(例如sizeof(++i),这个表达式不会被计算)。在编译阶段确定。

3. c和c++区别

         1. c是面向过程的语言,是一个结构化的语言,考虑如何通过一个过程对输入进行处理得到输出。C++是面向对象的语言,主要特征是封装、继承和多态。封装隐藏了实现细节,使得代码模块化。派生类可以继承父类的数据和方法,扩展了已经存在的模块,实现了代码复用。多态则是一个接口,多种实现。通过派生类重写父类的虚函数,实现了接口的重用。

         2. c和c++动态管理内存的方法不一样。C是使用malloc/free,而c++除此之外还有new/delete关键字。

         3. c++支持引用,c中不存在引用。

         4. c++支持函数重载,C语言不支持。

4. struct和class区别

         核心区别在于访问权限不同。Struct默认是public,class默认是private。

5. 三五法则

         1. 需要析构函数的类也需要拷贝构造函数和拷贝赋值函数。

2. 需要拷贝操作的类也需要赋值操作,反之亦然。

3. 析构函数不能是删除的

4. 如果一个类有删除的或不可访问的析构函数,那么其默认和拷贝构造函数会被定义为删除的。

5. 如果一个类有const或引用成员,则不能使用合成的拷贝赋值操作。

6. c++的static关键字的作用

         3个作用。

         1. 隐藏与隔离。Static函数和变量只在本文件可见,其它文件不可见。

         2. 保持变量内容的持久化。Static局部变量值只初始化一遍,后续调用使用上一次的保留值。

         3. 默认初始化。Static变量如果没有显示初始化,会被默认初始化为0。

7. 底层const和顶层const区别

         就是指针常量和常量指针的区别。指针常量表示一个指针,它是常量,这里的const是顶层属性。

         常量指针表示一个指针指向一个常量,这里的const属性表示底层const属性。

        

8. nullptr和NULL区别

         Nullptr是空指针。NULL在c++中是数值常量0。在某些情况下,使用NULL存在二义性问题。比如两个重载函数,参数分别是整型和指针类型。此时使用NULL存在二义性问题。

9. 指针与引用区别

         1. 指针是一个变量,引用只是变量的别名。

         2. 指针可以任意时刻赋值,引用只能在初始化的时候赋值,初始化后引用的值不能被改变。

         3. 访问指针指向的资源需要解引用,引用可以直接访问。

         4. 指针存在多级指针,引用只有一层。

10. 构造函数和析构函数能否调用虚函数

         不应该调用虚函数,构造函数的调用顺序是先调用基类,然后是派生类。虚函数主要用来在派生类中实现多态的。在构造函数中派生类还未生成,无法实现派生效果,因此没有意义。同理,析构函数调用顺序是先调用派生类析构函数,再调用基类析构函数。派生类已经销毁,此时,虚函数无法实现多态效果。

        

11. 构造函数能否声明为虚函数

         1. 构造对象的时候必须知道对象的类型,而虚函数的类型是在运行时确定的,在构造对象期间,编译器不知道构造的对象是基类还是派生类,因此不能声明为虚函数。

         2. 虚函数的运行依赖于虚函数指针,在构造函数执行期间,虚函数指针还未初始化,因此不能调用。

12. 为什么不把所有函数都声明为虚函数

         因为虚函数调用存在开销,比如说虚函数指针和虚表。

13. 哪些函数不能声明为虚函数

         1. 非成员函数,非成员函数只能被重载,不能被继承,而虚函数主要的作用是在继承中实现动态多态。非成员函数早在编译期间就已经绑定函数,无法实现动态多态,那声明成虚函数则无意义。

         2. 构造函数,理由和问题11一样。

         3. 静态成员函数,静态成员函数对于每个类来说只有一份,所有的对象共享一份代码。它是属于类而不属于对象。虚函数必须根据对象类型才能知道调用哪一个虚函数,故虚函数是一定要在对象的基础上。两者一个与实例相关,一个与类相关。

         4. 友元函数,c++不支持友元函数的继承,对于没有继承特性的函数没有虚函数的说法。友元函数不是类的成员函数,不能被继承。

       5. 内联函数,内联函数是为了在代码中直接展开,减少函数调用开销。虚函数是为了在继承后对象能够准确的执行自己的动作,并且inline函数在编译时被展开,虚函数在运行时才能动态绑定函数。

14. 构造函数哪些变量必须初始化

         Const变量和引用变量必须在初始化列表中初始化。

15. 析构函数能否抛出异常

         不能。两点。

         1. 异常点之后的代码不会被执行,存在资源泄漏可能。

         2. 异常之后如果再抛出异常,多个异常情况下,c++直接终止程序退出。

16. c++11构造函数有几种

         拷贝构造函数、移动构造函数等等。

17. 析构函数需要注意的地方

         1. 没有返回值。

         2. 没有参数。

         3. 不能抛出异常。

         4. 存在派生类的话需要声明为虚函数。

18. auto 与 decltype 的区别

         两者都是类型推导,区别在于auto会忽略对象的顶层const和引用属性,decltype会保存下来。

19. constexpr与const有何区别

         都用于定义常量。区别在于constexpr在编译期确定,const在运行时确定。

20. 面向对象的概念和基本特征

         概念:类、对象、继承。

         特征:封装、继承、多态。

21. 多态原理以及静态多态和动态多态

         多态即一个接口,多种实现。C++分为静态多态和动态多态。前者在编译期间绑定,后者在运行期间绑定。静态多态有两种实现方式,包括函数重载和函数模板。动态多态只有一种,即虚函数。

22. 内联函数和宏定义区别

         1. 宏定义是在预处理期间替换,内联函数是在编译期间替换。

         2. 宏定义仅作字符串替换,内联函数会有类型检查,更安全。

23. 菱形继承怎么解决

         菱形继承存在二义性问题。三种解决方式。

         1. 使用类和作用域限定符,明确调用对象。

         2. 在派生类中使用同名函数或变量覆盖基类成员。

         3. 使用虚继承机制。

24. 构造函数初始化顺序

         先初始化基类,然后初始化派生类。顺序按照类声明时候的顺序。

25. 解释静态类型和动态类型

         静态类型在编译期间确定,即声明时的类型。动态类型在运行期间确定,如虚函数。

26. 多重继承虚指针个数

         如果一个类拥有虚函数,则该类存在一个虚表,虚表中含有虚函数的地址。该类的每一个对象都存在一个虚函数指针,指向该类的虚表。多重继承情况下,派生类可能存在多个虚函数指针,指向各个基类的虚表。

27. C++空的结构体和空类大小,含有虚函数呢

         C++中空的结构体和空类大小为1,这是为了区分不同的空类或空结构体。含有虚函数的空对象大小为一个虚函数指针大小,32位操作系统是4,64位是8。注意,多重继承情况下,大小和虚函数指针个数有关。

28. extern “C”的作用

         C++支持函数重载,C语言不支持,不同的编译器编译出来的符号不一样。被Extern “C”修饰的变量或函数按照C语言编译方式编译,主要用来处理c和c++的混合编程问题。

29. new和malloc的区别

         1. new创建一个对象,malloc创建一块内存。

         2. new返回指向对象的指针,malloc返回指向void*的指针。

         3. new会自动调用对象的构造函数,需要使用delete删除。Malloc使用free删除。

         4. new是运算符,支持重载。Malloc是函数。

30. delete和delete[]区别

         Delete会调用对象的析构函数,delete[]会为数组内每一个元素调用析构函数。

31. 介绍悬垂指针和野指针

         悬垂指针表示指针所指向的内存已经被释放,野指针表示未初始化的指针。

32. c++虚函数和纯虚函数

         虚函数在函数声明前加上virtual关键字,主要用于多态。纯虚函数表示在虚函数的参数表后面加上=0,纯虚函数没有实现,含有纯虚函数的类为抽象基类。不能定义抽象基类的对象,抽象基类唯一的作用就是用于继承,派生类必须实现纯虚函数,否则,派生类也是抽象基类。

33. 静态成员函数和虚函数区别

         静态成员函数属于类,在编译期间确定。虚函数属于对象,在运行期间确定。

34. 虚继承和虚指针

         虚继承即在声明基类的时候加上virtual关键字,表示愿意和其它基类共享一份基类。虚基类必须由派生类中最外层的派生类初始化。虚继承主要用来解决菱形继承引起的二义性问题。

         含有虚函数的类有一个虚表,虚表中存储虚函数地址。这种类的对象有一个虚函数指针,虚函数指针指向虚表,可以实现动态多态。

35. c++重载和重写与覆盖的区别

         重载表示在同一个作用域中存在多个同名函数,这些同名函数仅在参数个数和参数类型上有区别。

         重写表示派生类重写父类的虚函数。

         覆盖表示派生类存在和父类同名的成员,仅名字相同。

36. 说一下虚函数表是什么

         含有虚函数的类存在一个虚表,虚表中存储虚函数地址。

37. c++如何防止内存泄漏

         C++提供了三个智能指针来解决。

         Share_ptr:多个share_ptr可以指向相同的资源,使用引用计数来表示共享的个数,只有引用计数为0的时候才会释放资源。

         Weak_ptr: weak_ptr指向share_ptr,主要用来解决share_ptr引起的循环引用问题。

         Unique_ptr:unique_ptr独占资源,不支持拷贝和赋值。

38. STL容器底层数据结构

         这个要好好学习《STL源码剖析》这本书,这本书介绍了c++中vector、list、deque、map、set等等的底层实现。

39. 描述stl模板以及优缺点

         这个我没有准备,感兴趣的童鞋参考《c++ primer》和《STL源码剖析》作为进阶。

40. STL中的迭代器失效问题

       Vector如果扩容的话会导致迭代器失效,list,map,set这些不会。

         Vector采用动态数组实现,扩容会重新分配内存,导致迭代器失效。

         List是双向链表,删除元素只会导致被删除元素迭代器失效,其它元素不会。

         Map和set的实现采用红黑树,删除元素不会影响其它元素的迭代器。

41. 介绍迭代器和容器之间的耦合关系

         这个懂不多。

42. 介绍一下allocator

         C++的allocator为两级空间配置器,这一块的内容在《STL源码剖析》第二章里讲的特别清楚。建议去阅读一下。Allocator的实现思想很重要,很多内存池的实现和它一样,包括Linux内核的内存管理算法,即伙伴关系算法。

         Allocator是空间配置器,分为两层。第一层主要用来分配大内存,默认是大于128字节的内存。

         第二层采用内存池和自由链表的实现。链表里包含小内存。每次申请内存的时候,如果大于128字节,则直接去一级配置器申请,否则,先查找对应的自由链表,如果存在空闲的内存块,则分配出去。否则,去内存池取一块。如果内存池不足的话,则会使用malloc向系统的堆申请。

43. vector的内存分配.

         Vector实现采用动态数组,当空间不足的时候,会重新分配内存,将元素拷贝过去。

44. hash_map和map的区别

         两点。

         1. hash_map采用数组加链表结构实现hash,采用拉链法解决hash冲突,map采用红黑树结构实现。

         2. hash_map元素是无序的,map元素是自动有序的。

45. hashmap底层实现原理

         采用数组加链表方式实现,当平衡因子大于等于1时,会引起数组扩容,扩容不是两倍,而是按照内置的质数数组取下一个元素。

46. hashmap存储结构怎么样,怎么处理的hash冲突,当查询时,其时间复杂度怎么样

         数组加链表,使用拉链法解决hash冲突,O(1)

47. 手写String类

48. 解释深拷贝和浅拷贝

         如果一个类对象含有一个指针变量。浅拷贝指的是拷贝该对象后,两个对象的指针指向同一个资源。深拷贝则拷贝指针指向的资源,拷贝后两对象的指针指向不同的内存,但是值一样。

         浅拷贝存在double free问题。

49. 介绍智能指针

         介绍share_ptr, weak_ptr, unique_ptr,auto_ptr等。具体内容建议各位童鞋阅读《c++ primer》

50. 循环引用如何解决

         使用weak_ptr

51. 手写share_ptr, unique_ptr,auto_ptr

52. vector list set 的区别

         这个从底层实现、性质以及迭代器失效方面去讲。具体内容参照《STL源码剖析》

53. 构造和析构需要注意点

         构造函数不能声明为虚函数,不能调用虚函数。此外,拷贝构造函数参数不能用值传递,否则导致死循环,正确做法时使用常量引用。

         析构函数注意点和17问一样。

54. c++四种转换

         Const_cast:常量转换主要用来去掉变量的const属性。

         Static_cast:静态转换主要用来处理基本数据类型的转换以及不含虚函数的基类和派生类的转换。

         Dynamic_cast:动态转换主要用来处理带虚函数的基类和派生类的转换。是唯一一种在运行时转换,会做动态检查。转换失败返回空指针。

         Reinterpret_cast:和C语言的强制类型一样,功能强大,但是不安全,不推荐使用。

55. 如何避免隐式转换

         使用explicit关键字。

56. 左值与右值     

         左值是一个变量,可以取地址。

         右值是一个临时变量,不能对其取地址。

57. 迭代器和智能指针的实现

         迭代器的实现了解就好。

         智能指针最好能够手写。

58. 模板类的偏特化

         主要涉及偏特化和全特化。熟悉c++的童鞋要掌握这个,了解c++的童鞋不用担心,面试官不会问这么深的内容。

59. Lambda特性以及使用场景

         这个我也讲不好,面试没被问过。但是熟悉c++的童鞋如果面试被问说不懂,场面会很尴尬。了解c++的童鞋知道就好。内容不难,看下《c++ primer》就行。

60. C++11 move 语义、右值引用等

         这两个特性主要用在移动构造函数里面,阅读《c++ primer》里注意下。

61. ++i和i++的区别

         《编程珠玑》里有讲这个,前置自增效率要高于后置自增,因为后置自增需要保存临时变量。

62. 手写单例模式

         代码就不给了。《剑指offer》第二道题就是。面鹅厂的时候考过,代码不多,一定要会。C++手写的面试题很有限,网上的面经99%都是单例模式、string类以及智能指针的实现等。

63. 字节对齐问题

         此类问题最常问的就是结构体占内存大小。两个原则,1. 结构体内部成员按照自身对齐。2. 结构体整体以最长成员对齐。具体参照网上博客。

================================================================================================

此外,对c++不了解的童鞋,有时间的话一定要阅读《c++ primer》和《STL源码剖析》这两本书,前者让你了解c++以及c++11各种特性,后者让你掌握STL内部实现,这两本书基本覆盖80%c++考点。另推荐《深度理解C++对象模型》这本书,对虚函数原理讲得不错,c++面试有20%左右问题涉及虚函数这一块,有空可以阅读,没空的话记一下虚函数的原理就行,反正也挺简单,网上的相关博客比较多。但是还是推荐阅读原书。

额外推荐参考书:
1. 《Primer c++》

2. 《STL源码剖析》

3. 《Effective C++》
4. 《深度理解C++对象模型》
5. 《程序员的自我修养》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值