设为首页 加入收藏

TOP

C++面试八股文:override和finial关键字有什么作用?(一)
2023-07-23 13:26:29 】 浏览:51
Tags:override finial 关键字

某日二师兄参加XXX科技公司的C++工程师开发岗位第22面: (二师兄好苦逼,节假日还在面试。。。)

面试官:C++的继承了解吗?

二师兄:(不好意思,你面到我的强项了。。)了解一些。

面试官:什么是虚函数,为什么需要虚函数?

二师兄:虚函数允许在基类中定义一个函数,然后在派生类中进行重写(override)。

二师兄:主要是为了实现面向对象中的三大特性之一多态。多态允许在子类中重写父类的虚函数,同样的函数在子类和父类实现不同的形态,简称为多态。

面试官:你知道overridefinial关键字的作用吗?

二师兄:override关键字告诉编译器,这个函数一定会重写父类的虚函数,如果父类没有这个虚函数,则无法通过编译。此关键字可省略,但不建议省略。

二师兄:finial关键字告诉编译器,这个函数到此为止,如果后续有类继承当前类,也不能再重写此函数。

二师兄:这两个关键字都是C++11引入的,为了提升C++面向对象编码的安全性。

面试官:你知道多态是怎么实现的吗?

二师兄:(起开,我要开始装逼了!)C++主要使用了虚指针和虚表来实现多态。在拥有虚函数的对象中,包含一个虚指针(virtual pointer)(一般位于对象所在内存的起始位置),这个虚指针指向一个虚表(virtual table),虚表中记录了虚函数的真实地址。

#include <iostream>
struct Foo
{
    size_t a = 42;
    virtual void fun1() {std::cout <<"Foo::fun1" << std::endl;}
    virtual void fun2() {std::cout <<"Foo::fun2" << std::endl;}
    virtual void fun3() {std::cout <<"Foo::fun3" << std::endl;}
};

struct Goo: Foo{
    size_t b = 1024;
    virtual void fun1() override {std::cout <<"Goo::fun1" << std::endl;}
    virtual void fun3() override {std::cout <<"Goo::fun3" << std::endl;}
};

using PF = void(*)();

void test(Foo* pf)
{
    size_t* virtual_point = (size_t*)pf;

    PF* pf1 = (PF*)*virtual_point;  
    PF* pf2 = pf1 + 1;  //偏移8字节 到下一个指针 fun2
    PF* pf3 = pf1 + 2;  //偏移16字节 到下下一个指针 fun3

    (*pf1)();   //Foo::fun1 or Goo::fun1 取决于pf的真实类型
    (*pf2)();   //Foo::fun2
    (*pf3)();   //Foo::fun3 or Goo::fun3 取决于pf的真实类型
}

int main(int argc, char const *argv[])
{
    Foo* fp = new Foo;
    test(fp);
    
    fp = new Goo;
    test(fp);

    size_t* virtual_point = (size_t*)fp;
    size_t* ap = virtual_point + 1;
    size_t* bp = virtual_point + 2;
    
    std::cout << *ap << std::endl;  //42
    std::cout << *bp << std::endl;  //1024
}

file

二师兄:当初始化虚表时,会把当前类override的函数地址写到虚表中(Goo::fun1Goo::fun3),对于基类中的虚函数但是派生类中没有override,则会把基类的函数地址写到虚表中(Foo::fun2),在调用函数的时候,会通过虚指针转到虚表,并根据虚函数的偏移得到真实函数地址,从而实现多态。

面试官:不错。上图你画出了单一继承的内存布局,那多继承呢?

二师兄:多继承内存布局类似,只不过会多几个virtual pointer

#include <iostream>
struct Foo1
{
    size_t a = 42;
    virtual void fun1() {std::cout <<"Foo1::fun1" << std::endl;}
    virtual void fun2() {std::cout <<"Foo1::fun2" << std::endl;}
    virtual void fun3() {std::cout <<"Foo1::fun3" << std::endl;}
};

struct Foo2{
    size_t b = 1024;
    virtual void fun4()  {std::cout <<"Foo2::fun4" << std::endl;}
    virtual void fun5()  {std::cout <<"Foo2::fun5" << std::endl;}
};

struct Foo3{
    size_t c = 0;
    virtual void fun6()  {std::cout <<"Foo3::fun1" << std::endl;}
    virtual void fun7()  {std::cout <<"Foo3::fun3" << std::endl;}
};

struct Goo: public Foo1, public Foo2, public Foo3
{
    virtual void fun2() override {std::cout <<"Goo::fun2" << std::endl;}
    virtual void fun6() override {std::cout <<"Goo::fun6" << std::endl;}
};

int main(int argc, char const *argv[])
{
    Goo g;
    g.fun1();   //Foo1::fun1
    g.fun2();   //Goo::fun2
    g.fun3();   //Foo1::fun3
    g.fun4();   //Foo2::fun4
    g.fun5();   //Foo2::fun5
    g.fun6();   //Goo::fun6
    g.fun7();   //Foo3::fun7
}

file

面试官:你知道什么是菱形继承吗?菱形继承会引发什么问题?如何解决?

二师兄:菱形继承(Diamond Inheritance)是指在继承层次结构中,如果两个不同的子类B和C继承自同一个父类A,而又有一个类D同时继承B和C,这种继承关

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇驱动开发:内核中进程与句柄互转 下一篇C++面试八股文:std::vector和std..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目