自动驾驶-机器人-slam-定位面经和面试知识系列03之C++STL面试题(01)

这两天有点忙耽搁了,抱歉!!!

这个博客系列会分为C++ STL-面经、常考公式推导和SLAM面经面试题等三个系列进行更新,基本涵盖了自己秋招历程被问过的面试内容(除了实习和学校项目相关的具体细节)。在知乎和牛客也会同步更新,全网同号(lonely-stone或者lonely_stone)。
关于高频面试题和C++ STL面经,每次我会更新10个问题左右,每次更新过多,害怕大家可能看了就只记住其中几个点。(在个人秋招面试过程中,面试到后面,发现除了个人项目和实习经历外,个人所记录的内容基本能涵盖面试官能问到的)
(另外个人才疏学浅,如果所分享知识中出现错误,请大家指出,避免误导其他人)

虚函数相关

1. 虚函数与多态

  • 虚函数和多态虚函数主要解决了什么问题?
    • 虚函数主要是用来实现多态和多重继承的,没有虚函数理论上也可以实现多态,但比较麻烦
    • 虚函数的目的是在基类指针指向派生类的时候还能正确调用跑派生类中实现的功能
  • 具体实现说明
    • 只需将基类中的成员函数声明为virtual就行
    • 基类中的析构函数必须为虚函数,否则会出现对象释放错误
    • 虚函数的使用将导致类对象占用更大的内存空间。(因为:编译器给每一个包括虚函数的对象添加了一个隐藏成员—指向虚函数表的指针也叫虚函数指针。虚函数表—virtual fuction table 包含了虚函数的地址,由所有拥有虚函数的对象共享。当派生类重新定义虚函数时,则将该函数的地址添加到虚函数表中)无论一个类对象中定义了多少个虚函数,虚函数指针只有一个。相应地,每个对象在内存中的大小要比没有虚函数时大4个字节(不包括虚析构函数)。
  • 子类重写(覆盖)虚函数的规则
    • 虚函数在子类和父类中的访问权限可以不同
    • 基类与派生类的虚函数名与参数列表相同。且函数返回值有以下要求:
      • 如果虚函数的返回值类型是基本数据类型:返回值类型必须相同
      • 如果虚函数的返回值类型是类本身的指针或引用:返回值类型可以不同,但派生类的返回值类型要小于基类返回值类型
  • 什么是多态,解决了什么问题
    • 多态就是一个指针指向子类对象,那么他调用的函数就是子类的对象的。实现了一个函数会根据传入参数的不同有不同的功能,函数有多个状态,就是多态。
    • 如果没有多态,就需要写多个同名函数,参数不同,就是重载。
    • 对于多态的状态来说,一定会有两个同名同参数函数,分别定义在子类和父类中
    • C++ 多态有两种:静态多态(早绑定)、动态多态(晚绑定)。静态多态是通过函数重载实现的;动态多态是通过虚函数实现的
  • 纯虚函数:
    • 纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加"=0"
    • 纯虚函数的意义,让所有的类对象(主要是派生类对象)都可以执行纯虚函数的动作,但类无法为纯虚函数提供一个合理的默认实现。所以类纯虚函数的声明就是在告诉子类的设计者,“你必须提供一个纯虚函数的实现,但我不知道你会怎样实现它”。
    • 我个人理解纯虚函数的话,在子类中定义以后成为虚函数,然后还可以在孙子类中覆盖该虚函数

2. 关键字virtual、override、final

  • virtual:放在函数的返回值前面,父类虚函数前必须写,子类可以省略(因为子类中不管写不写都是虚函数)。只能在类内部的声明语句之前,而不能用于类外部
  • override:父类的虚函数不可使用,放在子类虚函数的参数列表后。
  • final:如果我们定义的一个虚函数不想被派生类覆盖(重写),那么就要在参数列表后加一个final。
  • 回避虚函数的机制:虚函数就是为了实现多态的功能,但具体实行我们才知道他会运行那个函数,当我们想特定函数来运行的时候,就只能通过域运算符来实现。

3. 什么是重载运算符和重载函数?

  • 函数重载:在同一个作用域内,可以声明多个类似同名函数,但这些函数的形参(个数、类型或者顺序)必须不同。
  • 重载运算符:同一个作用域中的某个运算符指定多个定义,operator操作。

4. 基类为什么需要虚析构函数

防止内存泄漏。想去借助父类指针销毁子类对象的时候,不能去销毁子类对象。假如没有虚析构函数,释放一个由基类指针指向的派生类对象时,不会触发动态绑定,则只会调用基类的析构函数,不会调用派生类的。派生类中申请的空间则得不到释放导致内存泄漏。
为什么析构函数必须是虚函数?为什么c++默认的析构函数不是虚函数?

  • 将可能会被继承的父类的析构函数设置为虚函数,可以保证当我们new一个子类,然后使用基类指针指向该子类对象,释放基类指针时可以释放掉子类的空间,防止内存泄漏。
  • C++ 默认的析构函数不是虚函数是因为虚函数需要额外的虚函数表和虚表指针,占用额外的内存。而对于不会被继承的类来说,其析构函数如果是虚函数,就会浪费内存。因此c++默认的析构函数不是虚函数,而是只有需要当做父类时,才会被设置成虚函数。
  • 类析构顺序:派生类本身析构>对象成员析构函数>基类析构函数

5. 基类的虚函数表存放在内存的什么区,虚表指针vptr的初始化时间

首先整理一下虚函数表的特征:

  • 虚函数表是全局共享的元素,且全局只有一个,在编译时就能构造完成
  • 虚函数表类似于一个数组,类对象中存储vptr指针,指向虚函数表,即虚函数表不是函数,不是程序代码,不可能存储在代码段
  • 虚函数表存储虚函数的地址,即虚函数表的元素是指向类成员函数的指针,而类中虚函数的个数在编译时期就可以确定,即虚函数表的大小可以确定,即大小是在编译时期确定的,不必动态分配内存空间存储虚函数表,所以不在堆中。

根据以上特征,虚函数表类似于类中静态成员变量,静态成员变量也是全局共享,大小确定,因此最有有可能存储在全局数据区。
虚函数表vtabel在Linux/Unix中存放在可执行文件的制度数据段中(rodata),这与微软的编译器将虚函数表存放在常量段存在一些区别
由于虚表指针vptr跟虚函数密不可分,对于有虚函数或者继承于虚函数的基类,对该类进行实例化时,在构造函数执行时会对虚表指针进行初始化,并且存放在对象内存布局的最前面。

6. 简述一下虚表数量是什么

在 C++ 中,当一个类具有虚函数时,编译器会为该类创建虚表。
虚表是一个指针数组,其中的每个元素都是一个函数指针,指向该类的虚函数的实现。在类的对象被创建时,会为其分配一个虚函数指针,该指针指向该类的虚表。当调用虚函数时,程序会根据虚函数指针查找虚表,从而找到对应的函数实现。
因此,虚表数量就是一个类中虚函数的数量。对于一个类来说,虚表数量可能是0,也可能是多个。对于一个继承关系中的每个类,都可能有自己的虚表。
多线程则是多任务的一种实现方式。

你可能感兴趣的:(面试,c++,职场和发展)