C++面试汇总

1、CPP内存管理

参考回答:https://www.cnblogs.com/findumars/p/5929831.html?utm_source=itdadao&utm_medium=referral
参考回答:https://blog.csdn.net/hanoi_ahoj/article/details/88660494

类中的静态成员:静态成员变量只能在类中声名,不能在类中定义或者初始化,定义是为变量分配空间,而初始化是为变量赋值
详解参考博客:https://www.jianshu.com/p/feaf81b88f85

参考博客(C++空间配置代码分析):https://blog.csdn.net/haluoluo211/article/details/80560066
参考博客(C++空间配置代码分析):https://blog.csdn.net/haluoluo211/article/details/80573204
参考博客(C++vector容器实现分析):https://blog.csdn.net/haluoluo211/article/details/80635782
C++内存管理参考博客:https://www.cnblogs.com/findumars/p/5929831.html?utm_source=itdadao&utm_medium=referral

内存空间:

  • 内核空间
  • 内存映射区
  • 全局变量区(分为bss、data区,bss区存储未初始化或初始化为0的全局变量,运行时占用内存,但是可执行程序不占用空间大小,data区存放已赋值的全局变量,在运行时和可执行文件都占用空间)
  • 代码区

参考博客:https://blog.csdn.net/laiqun_ai/article/details/8528366

2、new/delete和malloc/free区别

参考博客:https://www.cnblogs.com/findumars/p/5929831.html?utm_source=itdadao&utm_medium=referral

3、C++多态,静态多态和动态多态都有什么(静态绑定、动态绑定)

C++的多态性用一句话概括就是:在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数。如果对象类型是派生类,就调用派生类的函数;如果对象类型是基类,就调用基类的函数
1:用virtual关键字申明的函数叫做虚函数,虚函数肯定是类的成员函数
2:存在虚函数的类都有一个一维的虚函数表叫做虚表,类的对象有一个指向虚表开始的虚指针。虚表是和类对应的,虚表指针是和对象对应的
3:多态性是一个接口多种实现,是面向对象的核心,分为类的多态性和函数的多态性
4:多态用虚函数来实现,结合动态绑定.
5:纯虚函数是虚函数再加上 = 0
6:抽象类是指包括至少一个纯虚函数的类
纯虚函数:virtual void fun()=0;即抽象类!必须在子类实现这个函数,即先有名称,没有内容,在派生类实现内容

静态多态就是在系统编译期间就可以确定程序执行到这里将要执行哪个函数,比如函数的重载。
动态多态则是利用虚函数实现了运行时的多态,也就是说在系统编译的时候并不知道程序将要调用哪一个函数,只有在运行到这里的时候才能确定接下来会跳转到哪一个函数的栈帧

注意:C语言不支持重载,但支持变长参数:
多态有类的多态和函数的多态,函数的多态是指一个函数被定义成不同参数的函数,当调用函数时,根据不同的参数调用不同的函数

那些函数不能定义为虚函数?
经检验下面的几个函数都不能定义为虚函数:
1)友元函数,它不是类的成员函数
2)全局函数
3)静态成员函数,它没有this指针
3)构造函数,拷贝构造函数,以及赋值运算符重载(可以但是一般不建议作为虚函数)

参考博客:https://www.cnblogs.com/cxq0017/p/6074247.html
参考博客:https://blog.csdn.net/qq_39412582/article/details/81628254

4、类的虚函数的作用;B类继承A类,AB类中均定义S函数;内部机制(虚函数表)**

参照第3问
**基类析构函数定义为虚函数的原因:**基类指针可能指向派生类,当delete的时候,如果不定为虚函数,系统会直接调用基类的析构函数,这个时候派生类就有一部分没有被释放,就会造成可怕的内存泄漏问题
若定义为虚函数,那么就会先调用派生类的析构函数然后派生类的析构函数会自动调用基类的析构函数,这个结果满足我们的本意

所以!在继承的时候,尽量把基类的析构函数定义为虚函数,这样继承下去的派生类的析构函数也会被变成虚函数构成多态

参考博客(C++多态):https://blog.csdn.net/skySongkran/article/details/82012698
参考博客(C++多态):https://blog.csdn.net/qq_39412582/article/details/81628254
参考博客(C++多态):https://blog.csdn.net/weixin_42678507/article/details/89414998

5、四种强制类型转换:静态转换、常量转换、重解释转换、动态转换**
  • static_cast转换:可用于基本数据类型的转换,也可用于基类和派生间的转换(父类、子类)
    在进行类间转换时:
    上行转换(派生类---->基类)是安全的
    下行转换(基类---->派生类)由于没有动态类型检查,所以是不安全的
    隐式转换都建议使用static_cast进行显示转换
  • dynamic_cast转换:只有在派生类之间转换时才使用dynamic_cast,type-id必须是类指针,类引用或者void*
    基类必须要有虚函数,因为dynamic_cast是运行时类型检查,需要运行时类型信息,而这个信息是存储在类的虚函数表中,只有一个类定义了虚函数,才会有虚函数表
    对于下行转换,dynamic_cast是安全的(当类型不一致时,转换过来的是空指针),而static_cast是不安全的(当类型不一致时,转换过来的是错误意义的指针,可能造成踩内存,非法访问等各种问题)
  • const_cast转换:const_castexpression
    使用场景:
      a、常量指针转换为非常量指针,并且仍然指向原来的对象
      b、常量引用被转换为非常量引用,并且仍然指向原来的对象
    使用特点:
      a、cosnt_cast是四种类型转换符中唯一可以对常量进行操作的转换符
      b、去除常量性是一个危险的动作,尽量避免使用。一个特定的场景是:类通过const提供重载时,一般都是非常量函数调用const_cast将参数转换为常量,然后调用常量函数,然后得到结果再调用const_cast 去除常量性
  • reinterpret_cast转换:reinterpret_castexpression
    使用场景:不到万不得已,不用使用这个转换符,高危操作
    使用特点:  
      a、reinterpret_cast是从底层对数据进行重新解释,依赖具体的平台,可移植性差
      b、reinterpret_cast可以将整型转换为指针,也可以把指针转换为数组
      c、reinterpret_cast可以在指针和引用里进行肆无忌惮的转换

回答参考:https://www.cnblogs.com/cauchy007/p/4968707.html

6、inline内联函数

内联函数:

  • 内联函数在编译时进行类型检查和自动类型转换
  • 内联可以调试
  • 可访问成员变量

宏定义:只在预编译的时候进行替换

参考博客:https://www.jianshu.com/p/89d2c1d8b202
参考博客(内联函数和宏定义):https://www.cnblogs.com/xuewangkai/p/11151246.html

7、代码到生成可执行文件的流程

C++的编译过程包括
step1 预编译
step2 编译
step3 汇编
step4 链接

预编译:主要展开头文件、宏定义等。汇编:把已经预编译的文件编译成汇编代码,整个过程包含语法、词法的分析,和一些优化操作
编译:将汇编代码变成目标代码,即生成二进制文件(.obj)
链接:将单个编译后的文件链接成一个可执行文件
前面的预编译、汇编、编译都是针对单个文件,以一个文件为一个编译单元,而链接则是将所有关联到的编译后的单元文件和应用到的库文件,进行一次链接处理,之前编译过的文件如果有用到其他文件里面定义到的函数、全局变量,在这个过程中会进行解析

  1. 预编译
    将.c 文件转化成 .i文件
    使用的gcc命令是:gcc –E
    对应于预处理命令cpp

  2. 编译
    将.c/.h文件转换成.s文件
    使用的gcc命令是:gcc –S
    对应于编译命令 cc –S

  3. 汇编
    将.s 文件转化成 .o文件
    使用的gcc 命令是:gcc –c
    对应于汇编命令是 as

  4. 链接
    将.o文件转化成可执行程序
    使用的gcc 命令是: gcc
    对应于链接命令是 ld

总结起来编译过程就上面的四个过程:预编译处理(.c) --> 编译、优化程序(.s、.asm)--> 汇编程序(.obj、.o、.a、.ko) --> 链接程序(.exe、.elf、.axf等)

参考博客:https://blog.csdn.net/gongjiwei/article/details/98845482
参考博客:https://blog.csdn.net/u012662731/article/details/78520349

8、C++协变与逆变

如果类型 Car 是类型 Vehicle 的子类型(subtype,Car ≤ Vehicle,可以在任何出现 Vehicle 的地方用 Car 代替),那么关于 Car 和 Vehicle 的复杂类型(如 std::vector 和 std::vector)之间的关系如下:

  • std::vector 是 std::vector 的子类型,所有出现 std::vector 的地方都可以用 std::vector 代替,即代替方向一致,则称之为协变(covariance)
  • std::vector 是 std::vector 的子类型,所有出现 std::vector 的地方都可以用 std::vector 代替,即代替方向相反,则称之为逆变(cotravariance)
  • std::vector 和 std::vector 之间没有关系,则称之为不变(invariance)

参考博客:https://www.jianshu.com/p/db76a8b08694

9、什么是智能指针? 智能指针的原理什么? 使用智能指针的目的是什么? 使用智能指针一定可以防止内存泄露吗? (什么情况下使用智能指针仍然会导致泄露)**

参考博客:https://www.jianshu.com/p/bf8de014e5c2
参考博客:https://www.cnblogs.com/wxquare/p/4759020.html
参考博客:https://blog.csdn.net/flowing_wind/article/details/81301001

10、了解的C++11新特性(说的智能指针)
11、如何实现一个只能在堆上建立实例的类**

参考博客:https://blog.csdn.net/c_base_jin/article/details/86501333
参考博客:https://www.cnblogs.com/luxiaoxun/archive/2012/08/03/2621827.html

12、设计模式了解哪些

单例模式、工厂模式、抽象工厂模式、备忘录模式、观察者模式、策略模式

13、什么是死锁?

参考博客:https://blog.csdn.net/hd12370/article/details/82814348

14、TCP UDP区别

参考博客:https://blog.csdn.net/gongjiwei/article/details/98896267

15、MMap

mmap把文件的部分内存映射到进程虚拟地址空间中,用户可以直接操作虚拟地址空间中的数据
mmap的写操作流程:

  • 用户把数据写入use buffer
  • 若虚拟地址在物理内存中,则直接拷贝到物理内存
  • 若虚拟地址不在物理内存中,则执行缺页中断,由系统内核处理
  • 由系统将脏页写回磁盘
    mmap的读操作流程
  • 直接读mmap对应的虚拟地址
  • 若对应物理内存有数据,则直接读,
  • 若不在,则由系统内核将数据从磁盘读入到物理内存,用户直接读

在小于2KB或4KB大小情况下,速度快与write系统调用,在大于2KB或4KB大小时,速度略慢于write系统调用
mmap的读操作速度远快于read系统调用,因为
参考博客:https://www.jianshu.com/p/eece39beee20
参考博客:https://blog.csdn.net/qq_33611327/article/details/81738195

16、进程和线程,说下多线程共享的资源

17、C++里面的const关键字,能修饰volatile吗?
参考博客:https://www.jianshu.com/p/1b9fcd074eb2

18、C++里面的内存泄露?

**内存泄漏的定义:**一般我们常说的内存泄漏是指堆内存的泄漏。堆内存是指程序从堆中分配的,大小任意的(内存块的大小可以在程序运行期决定),使用完后必须显示释放的内存。应用程序一般使用malloc,realloc,new等函数从堆中分配到一块内存,使用完后,程序必须负责相应的调用free或delete释放该内存块,否则,这块内存就不能被再次使用,我们就说这块内存泄漏了

**检测内存泄漏:**检测内存泄漏的关键是要能截获住对分配内存和释放内存的函数的调用。截获住这两个函数,我们就能跟踪每一块内存的生命周期,比如,每当成功的分配一块内存后,就把它的指针加入一个全局的list中;每当释放一块内存,再把它的指针从list中删除。这样,当程序结束的时候,list中剩余的指针就是指向那些没有被释放的内存。这里只是简单的描述了检测内存泄漏的基本原理,详细的算法可以参见Steve Maguire的<>

参考博客:https://www.cnblogs.com/findumars/p/5929831.html?utm_source=itdadao&utm_medium=referral

19、C的malloc和C++ new的区别?

20、父类指针指向子类对象,用该指针调用子类的方法? 不会(dynamic_cast)

21、解释一下堆栈**

参考博客:https://www.cnblogs.com/findumars/p/5929831.html?utm_source=itdadao&utm_medium=referral

22、epoll和select的区别

23、b 树和b+树的区别

24、scala和Cpp的区别

25、线程池的设计与相关问题

26、引用和指针区别 有常量引用吗?指针常量和常量指针

**大对象用const引用传递代替按值进行函数参数传递:**因为值传递会生成很多临时对象,进行内存拷贝,而且如果类中使用动态内存分配,还容易引发内存泄漏,因此,使用引用传递可以减小临时对象带来的开销,同时避免引发内存泄漏

27、一致性哈希

28、析构函数可以抛出异常吗
不可以,析构函数抛出异常将导致内存不能释放

29、索引失效情况

30、数据库的存储过程

31、volatile关键字

32、如果c++ 当中判断一个类型有没有某个方法你有哪些方式可以做到?发散一下思维,这个问题可能比较难

33、设计一个线程安全的HashMap

34、vector的内存是怎么管理的?底层的实现

35、STL中unique函数的实现

36、进程空间的理解,怎么求一个进程可用栈空间的大小?

37、MySQL数据库引擎说一下

38、表过大会有什么影响、怎么解决(我说了水平分表和垂直分表详细解释)

39、水平分表固定哈希,如果想要加表、删表怎么办

40、协程和线程的区别

41、一个二叉树上求两个节点之间路径长度的最大值,说出具体的思路

42、STL function有个特点,他的接口都是iterator,但是在用的时候传入容器的iterator或者说传入数据的指针都可以work这个背后的机理是什么?

43、模板当中对类型操作的思考

44、现在假设有一个程序,编译好的,编译没有错误,但是运行的时候报错,报的错是你正在调用一个纯虚函数,请问这里面导致这个错误的原因可能是什么?根据c++内部原理推理这个问题

45、描述一下,子类构造的时候,整个构造的过程,先怎么样,再怎么样,说清楚,是先构造父类的虚表指针还是先构造父类的成员?虚表指针是什么时候设进去的? 在构造函数当中一部分是初始化列表一部分是在花括弧里面,你能说一下这些的顺序是什么么?差别是什么 和this指针的顺序?初始化列表的写法和顺序有没有什么关系?

46、了解什么rpc方式

47、c++的内存管理有了解过么?

48、new一个对象的时候该对象在内存当中是怎么布局的

49、成员变量,虚函数表在哪个位置?

50、一个类可能会有父类和子类那么这些信息在对象当中是如何排版的呢?

51、在子类的构造函数中调用虚函数,调用的是父类的实现还是子类的实现

52、虚函数指针和构造函数体那个先被构造?

53、 c++运行构造函数的时候虚函数表被构造出来了么?

54、什么是TCP粘包

55、什么是平衡二叉树

56、单链表如何判断有环?环的位置在哪?

57、栈和堆的理解,知不知道栈帧

58、查看Linux系统的网络连接信息
用netstat命令和ss命令,netstat 命令:用于显示各种网络相关信息,如网络连接,路由表,接口状态,无效连接,组播成员 等等
ss命令可以用来获取socket统计信息,它可以显示和netstat类似的内容。但是ss的优势在于它能够显示更详细的有关网络连接的状态信息,而比netstat更快速、更高效
参考博客:https://www.cnblogs.com/yangleitao/p/9583280.html

22、谈一下磁盘故障预测的过程

23、谈一下ceph bluestore和filestore的区别,filestore为什么使用日志,以及分布式存储中,日志的作用

24、内存屏障

25、谈一下块设备为什么叫块设备,以及ssd和hdd在顺序写和随机写方面的性能差别,以及原因

26、块设备为什么叫块设备,写块设备时,若所写位置的开始地址不是块对齐,如何写到磁盘中。(面试官说这题是加分题,我只回答出来了方法,代码没有写出来,不知道有没有加分)

27、谈一下ceph底层存储的方式,以及将simple write改为append-only形式有何优势

28、讲讲STL的容器

面试经验:https://www.nowcoder.com/discuss/188367?type=2&order=0&pos=13&page=3

学习资料:https://www.nowcoder.com/discuss/193598?type=2&order=0&pos=2&page=2

doc格式,60多页吧,几百道题吧,都有答案吧,看好在下!部分:1.求下面函数的返回值(微软)int func(x) { int countx = 0; while(x) { countx ++; x = x&(x-1); } return countx; } 假定x = 9999。 答案:8思路:将x转化为2进制,看含有的1的个数。2. 什么是“引用”?申明和使用“引用”要注意哪些问题?答:引用就是某个目标变量的“别名”(alias),对应用的操作与对变量直接操作效果完全相同。申明一个引用的时候,切记要对其进行初始化。引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,不能再把该引用名作为其他变量名的别名。声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。不能建立数组的引用。3. 将“引用”作为函数参数有哪些特点?(1)传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。(2)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。(3)使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用"*指针变量名"的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。4. 在什么时候需要使用“常引用”? 如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。常引用声明方式:const 类型标识符 &引用名=目标变量名;例1int a ;const int &ra=a;ra=1; //错误a=1; //正确 例2string foo( );void bar(string & s); 那么下面的表达式将是非法的:bar(foo( ));bar("hello world"); 原因在于foo( )和"hello world"串都会产生一个临时对象,而在C++中,这些临时对象都是const类型的。因此上面的表达式就是试图将一个const类型的对象转换为非const类型,这是非法的。引用型参数应该在能被定义为const的情况下,尽量定义为const 。5. 将“引用”作为函数返回值类型的格式、好处和需要遵守的规则?格式:类型标识符 &函数名(形参列表及类型说明){ //函数体 }好处:在内存中不产生被返回值的副本;(注意:正是因为这点原因,所以返回一个局部变量的引用是不可取的。因为随着该局部变量生存期的结束,相应的引用也会失效,产生runtime error!注意事项:(1)不能返回局部变量的引用。这条可以参照Effective C++[1]的Item 31。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了"无所指"的引用,程序会进入未知状态。 (2)不能返回函数内部new分配的内存的引用。这条可以参照Effective C++[1]的Item 31。虽然不存在局部变量的被动销毁问题,可对于这种情况(返回函数内部new分配内存的引用),又面临其它尴尬局面。例如,被函数返回的引用只是作为一个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memory leak。(3)可以返回类成员的引用,但最好是const。这条原则可以参照Effective C++[1]的Item 30。主要原因是当对象的属性是与某种业务规则(business rule)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。(4)流操作符重载返回值申明为“引用”的作用:流操作符<>,这两个操作符常常希望被连续使用,例如:cout << "hello" << endl; 因此这两个操作符的返回值应该是一个仍然支持这两个操作符的流引用。可选的其它方案包括:返回一个流对象和返回一个流对象指针。但是对于返回一个流对象,程序必须重新(拷贝)构造一个新的流对象,也就是说,连续的两个<<操作符实际上是针对不同对象的!这无法让人接受。对于返回一个流指针则不能连续使用<<操作符。因此,返回一个流对象引用是惟一选择。这个唯一选择很关键,它说明了引用的重要性以及无可替代性,也许这就是C++语言中引入引用这个概念的原因吧。 赋值操作符=。这个操作符象流操作符一样,是可以连续使用的,例如:x = j = 10;或者(x=10)=100;赋值操作符的返回值必须是一个左值,以便可以被继续赋值。因此引用成了这个操作符的惟一返回值选择。例3#i nclude int &put(int n);int vals[10];int error=-1;void main(){put(0)=10; //以put(0)函数值作为左值,等价于vals[0]=10; put(9)=20; //以put(9)函数值作为左值,等价于vals[9]=20; cout<<vals[0]; cout<<vals[9];} int &put(int n){if (n>=0 && n<=9 ) return vals[n]; else { cout<<"subscript error"; return error; }} (5)在另外的一些操作符中,却千万不能返回引用:+-*/ 四则运算符。它们不能返回引用,Effective C++[1]的Item23详细的讨论了这个问题。主要原因是这四个操作符没有side effect,因此,它们必须构造一个对象作为返回值,可选的方案包括:返回一个对象、返回一个局部变量的引用,返回一个new分配的对象的引用、返回一个静态对象引用。根据前面提到的引用作为返回值的三个规则,第2、3两个方案都被否决了。静态对象的引用又因为((a+b) == (c+d))会永远为true而导致错误。所以可选的只剩下返回一个对象了。6. “引用”与多态的关系?引用是除指针外另一个可以产生多态效果的手段。这意味着,一个基类的引用可以指向它的派生类实例。例4Class A; Class B : Class A{...}; B b; A& ref = b;7. “引用”与指针的区别是什么?指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。此外,就是上面提到的对函数传ref和pointer的区别。8. 什么时候需要“引用”?流操作符<>、赋值操作符=的返回值、拷贝构造函数的参数、赋值操作符=的参数、其它情况都推荐使用引用。以上 2-8 参考:http://blog.csdn.net/wfwd/archive/2006/05/30/763551.aspx9. 结构与联合有和区别?1. 结构和联合都是由多个不同的数据类型成员组成, 但在任何同一时刻, 联合中只存放了一个被选中的成员(所有成员共用一块地址空间), 而结构的所有成员都存在(不同成员的存放地址不同)。 2. 对于联合的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于结构的不同成员赋值是互不影响的。10. 下面关于“联合”的题目的输出?a)#i nclude union{int i;char x[2];}a;void main(){a.x[0] = 10; a.x[1] = 1;printf("%d",a.i);}答案:266 (低位低地址,高位高地址,内存占用情况是Ox010A)………………
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值