设为首页 加入收藏

TOP

C++面试八股文:override和finial关键字有什么作用?(二)
2023-07-23 13:26:29 】 浏览:53
Tags:override finial 关键字
系被称为菱形继承。

file

二师兄:因为B和C各继承了一份A,当D继承B和C的时候就会有2份A;

#include <iostream>
struct A
{
    int val = 42;
    virtual void fun(){std::cout <<"A::fun" << std::endl;}
};
struct B: public A{ void fun() override{std::cout <<"B::fun" << std::endl;}};
struct C: public A{ void fun() override{std::cout <<"C::fun" << std::endl;}};
struct D: public B, public C{void fun() override{std::cout <<"D::fun" << std::endl;}};
int main(int argc, char const *argv[])
{
   	D d;
    std::cout << d.val << std::endl;	//编译失败,不知道调用从哪个类中继承的val变量
    d.fun(); 	//编译失败,不知道调用从哪个类中继承的fun函数
}

二师兄:解决的办法有两种,一种是在调用符之前加上父类限定符:

std::cout << d.B::val << std::endl; //42
d.C::fun();     //C::fun

二师兄:但这里并没有解决数据冗余的问题,因为D中有B和C,而B和C各有一个虚表和一个int类型的成员变量,所以sizeof(D)的大小是32(x86_64架构,考虑到内存对齐)。

二师兄:所幸在C++11引入了虚继承(Virtual Inheritance)机制,从源头上解决了这个问题:

#include <iostream>
struct A
{
    int val = 42;
    virtual void fun(){std::cout <<"A::fun" << std::endl;}
};
struct B: virtual public A{ void fun() override{std::cout <<"B::fun" << std::endl;}};
struct C: virtual public A{ void fun() override{std::cout <<"C::fun" << std::endl;}};
struct D: public B, public C{void fun() override{std::cout <<"D::fun" << std::endl;}};
int main(int argc, char const *argv[])
{
    D d;
    std::cout << d.val << std::endl; //42
    d.fun();     //D::fun
}

二师兄:此时在对象d中,只包含了一个val和两个虚指针,成员变量的冗余问题得到解决。

面试官:一般我们认为多态会影响性能,你举得为什么影响性能?

二师兄:大多数人认为,虚函数的调用会先通过虚指针跳到虚函数表,然后通过偏移确定函数真实地址,再跳转到地址执行,是间接调用导致了性能损失。

二师兄:但实际上无法内联才是虚函数性能低于正常函数的主要原因。由于多态是运行时特征,在编译时编译器并不知道指针指向的函数地址,所以无法被内联。同时跳转到特定地址执行函数可能引发的L1 cache miss(空间局部性不好),这也会影响性能。

面试官:虚函数的调用一定是非内联的吗?

二师兄:不是。现代编译器很聪明,如果编译器能够在编译时推断出真实的函数,可能会直接内联这个虚函数。虚函数的调用是否内联取决于编译器的实现和上下文。

面试官:你觉得多态在安全性上有没有什么问题?

二师兄:的确是有的。当我们把类中的虚函数定义为private的时候,虽然我们不能通过类的对象去访问这个函数,但我们知道这个函数就在虚函数表中,可以通过特殊的方法(上文中已经给出示例)访问它:

#include <iostream>
struct Foo
{
private:
    virtual void fun() {std::cout << "Foo::fun" << std::endl;}
};

int main(int argc, char const *argv[])
{
    Foo f;
    //f.fun();  //编译错误
    using Fun = void(*)();
    size_t* virtual_point = (size_t*)&f;
    Fun* fun = (Fun*)*virtual_point;
    (*fun)();
}

面试官:好的,今天的面试到这里就结束了,请回去等通知吧。

今天二师兄表现很不错,加个肉粽。感谢小伙伴的耐心阅读,祝各位小伙伴端午节牛逼(端午快乐->没文化,端午安康->跟风狗,好吧我祝各位端午牛逼)。二师兄的C++面试之旅,明天不见不散

关注我,带你21天“精通”C++!(狗头)

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

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目