【第三章:BAT等名企面试真题解析8讲】第8节:阿里面试真题解析之C++相关问题

前言

    阿里虽然是国内Java的第一大厂但是并非所有的业务都是由Java支撑,很多服务和中下层的存储,计算,网络服务,大规模的分布式任务都是由C++编写。在阿里所有部门当中对C++考察最深的可能就是阿里云。在此我将以我今年实习和秋招面试阿里云的真题进行分析,通过真题对面试当中遇到的C++问题进行解答。
 

C++面试重点

我本人是以C++作为主编程语言。C++是后台开发以及基础架构方向使用较多的语言之一。在我所有的面试当中,对于C++语言的考察主要集中在以下几点:
1.STL 容器相关实现
2.C++新特性的了解
3.多态和虚函数的实现
4.指针的使用

阿里面试真题再现

问题1:现在假设有一个编译好的C++程序,编译没有错误,但是运行时报错,报错如下:你正在调用一个纯虚函数(Pure virtual function call error),请问导致这个错误的原因可能是什么?

答:纯虚函数调用错误一般由以下几种原因导致:

  1. 从基类构造函数直接调用虚函数。
  2. 从基类析构函数直接调用虚函数。
  3. 从基类构造函数间接调用虚函数。
  4. 从基类析构函数间接调用虚函数。
  5. 通过悬空指针调用虚函数。

其中1,2编译器会检测到此类错误。3,4,5编译器无法检测出此类情况,会在运行时报错。

直接调用指的是函数内部直接调用虚函数,间接调用指的是函数内部调用其他的非虚函数内部直接或间接调用了虚函数。


解析:

    首先对关键知识点进行回顾:虚函数表,对象构造和析构过程

虚函数表vtbl:

  1. 编译器在编译时期为每个带虚函数的类创建一份虚函数表
  2. 实例化对象时, 编译器自动将类对象的虚表指针指向这个虚函数表

构造一个派生类对象的过程:

1.构造基类部分:

  1. 将实例的虚表指针指向基类的vtbl
  2. 构造基类的成员变量
  3. 执行基类的构造函数函数体

2.递归构造派生类部分:

  1. 将实例的虚表指针指向派生类vtbl
  2. 构造派生类的成员变量
  3. 执行派生类的构造函数体

析构一个派生类对象的过程:

1.递归析构派生类部分:

  1. 将实例的虚表指针指向派生类vtbl
  2. 执行派生类的析构函数体
  3. 析构派生类的成员变量

2.析构基类部分:

  1. 将实例的虚表指针指向基类的vtbl
  2. 执行基类的析构函数函数体
  3. 析构基类的成员变量

    由以上可知在构造函数和析构函数执行函数体过程时,实例的虚表指针指向的是构造函数和析构函数本身所属的那部分的类的虚函数表,此时执行的虚函数都实际调用的是该类本身的虚函数,所以如果在基类的析构或者构造函数当中调用虚函数且该虚函数本身在基类当中是纯虚函数那么就会出现纯虚函数调用。


可以运行如下代码进行验证:
#include <iostream>
using namespace std;

class Parent {
    public:
    virtual void virtualFunc() = 0;
    void helper() {
        virtualFunc();
    }
    virtual ~Parent(){
        helper();
    }
};

class Child : public Parent{
    public:
    void virtualFunc() {
        cout << "Child" << endl;
    }
    virtual ~Child(){}
};


int main() {

    Child child;
    return 0;
}

运行时报错:libc++abi.dylib: Pure virtual function called!


通过悬空指针调用虚函数:

悬空指针:指针最初指向的内存已经被释放了的一种指针, 访问"不安全可控"的内存区域将导致未定义的行为。

如下代码显示了悬空指针调用虚函数的典型案例:

#include <iostream>
using namespace std;

class Parent {
public:
    virtual void virtualFunc() = 0;
    void testFunc(){};
    virtual ~Parent(){
        testFunc();
    };
};

class Child : public Parent{
public:
    virtual void virtualFunc() {
        cout << "Child-VirtualFunc-call" << endl;
    }
    virtual ~Child(){};
};


int main() {

    Parent* child = new Child();
    Parent* p = child;
  //p此时可以成功的调用Child的virtualFunc输出"Child-VirtualFunc-call"
    p->virtualFunc();
    delete child;
  //在delete child之后p

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

<p> 《收割BAT:C++校招学习路线总结》,专刊共计17节。专刊分为五大主要内容,包括后台开发学习路线、简历制作,面试技巧、BAT等名企面试真题解析和工作学习常用工具。本专刊将介绍我在技术成长过程当中的经验,通关BAT的面试技巧,并结合亲身经历的面试真题由浅入深的讲解后台开发方向的重点问题,让你们的求职之路更加顺畅。 本专刊购买后即可解锁所有章节,故不可以退换哦~ </p> <p> <br /> </p>

全部评论
博主你好,我有一个问题:父类的析构函数声明为虚函数,那么子类的析构函数是否为虚函数?我的理解是子类析构函数函数不是父类析构函数的重写。所以不是虚析构函数。这样的话如果继承链大于2时为了实现多态每一个中间类的析构函数都应该显示声明为虚函数。
点赞 回复 分享
发布于 2021-11-08 17:50

相关推荐

点赞 评论 收藏
分享
评论
5
6
分享

创作者周榜

更多
牛客网
牛客企业服务