最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++面試八股文之override和finial關(guān)鍵字有何作用

 更新時(shí)間:2023年06月23日 09:04:17   作者:binarch  
C++11中的override和final關(guān)鍵字是為了增強(qiáng)代碼的編譯時(shí)類型檢查和面向?qū)ο笤O(shè)計(jì)中的繼承機(jī)制,下面這篇文章主要給大家介紹了關(guān)于C++面試八股文之override和finial關(guān)鍵字有何作用的相關(guān)資料,需要的朋友可以參考下

某日二師兄參加XXX科技公司的C++工程師開發(fā)崗位第22面: (二師兄好苦逼,節(jié)假日還在面試。。。)

面試官:C++的繼承了解嗎?

二師兄:(不好意思,你面到我的強(qiáng)項(xiàng)了。。)了解一些。

面試官:什么是虛函數(shù),為什么需要虛函數(shù)?

二師兄:虛函數(shù)允許在基類中定義一個(gè)函數(shù),然后在派生類中進(jìn)行重寫(override)。

二師兄:主要是為了實(shí)現(xiàn)面向?qū)ο笾械娜筇匦灾欢鄳B(tài)。多態(tài)允許在子類中重寫父類的虛函數(shù),同樣的函數(shù)在子類和父類實(shí)現(xiàn)不同的形態(tài),簡稱為多態(tài)。

面試官:你知道overridefinial關(guān)鍵字的作用嗎?

二師兄:override關(guān)鍵字告訴編譯器,這個(gè)函數(shù)一定會(huì)重寫父類的虛函數(shù),如果父類沒有這個(gè)虛函數(shù),則無法通過編譯。此關(guān)鍵字可省略,但不建議省略。

二師兄:finial關(guān)鍵字告訴編譯器,這個(gè)函數(shù)到此為止,如果后續(xù)有類繼承當(dāng)前類,也不能再重寫此函數(shù)。

二師兄:這兩個(gè)關(guān)鍵字都是C++11引入的,為了提升C++面向?qū)ο缶幋a的安全性。

面試官:你知道多態(tài)是怎么實(shí)現(xiàn)的嗎?

二師兄:(起開,我要開始裝逼了?。〤++主要使用了虛指針和虛表來實(shí)現(xiàn)多態(tài)。在擁有虛函數(shù)的對(duì)象中,包含一個(gè)虛指針(virtual pointer)(一般位于對(duì)象所在內(nèi)存的起始位置),這個(gè)虛指針指向一個(gè)虛表(virtual table),虛表中記錄了虛函數(shù)的真實(shí)地址。

#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字節(jié) 到下一個(gè)指針 fun2
    PF* pf3 = pf1 + 2;  //偏移16字節(jié) 到下下一個(gè)指針 fun3
    (*pf1)();   //Foo::fun1 or Goo::fun1 取決于pf的真實(shí)類型
    (*pf2)();   //Foo::fun2
    (*pf3)();   //Foo::fun3 or Goo::fun3 取決于pf的真實(shí)類型
}
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
}

二師兄:當(dāng)初始化虛表時(shí),會(huì)把當(dāng)前類override的函數(shù)地址寫到虛表中(Goo::fun1、Goo::fun3),對(duì)于基類中的虛函數(shù)但是派生類中沒有override,則會(huì)把基類的函數(shù)地址寫到虛表中(Foo::fun2),在調(diào)用函數(shù)的時(shí)候,會(huì)通過虛指針轉(zhuǎn)到虛表,并根據(jù)虛函數(shù)的偏移得到真實(shí)函數(shù)地址,從而實(shí)現(xiàn)多態(tài)。

面試官:不錯(cuò)。上圖你畫出了單一繼承的內(nèi)存布局,那多繼承呢?

二師兄:多繼承內(nèi)存布局類似,只不過會(huì)多幾個(gè)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
}

面試官:你知道什么是菱形繼承嗎?菱形繼承會(huì)引發(fā)什么問題?如何解決?

二師兄:菱形繼承(Diamond Inheritance)是指在繼承層次結(jié)構(gòu)中,如果兩個(gè)不同的子類B和C繼承自同一個(gè)父類A,而又有一個(gè)類D同時(shí)繼承B和C,這種繼承關(guān)系被稱為菱形繼承。

二師兄:因?yàn)锽和C各繼承了一份A,當(dāng)D繼承B和C的時(shí)候就會(huì)有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;	//編譯失敗,不知道調(diào)用從哪個(gè)類中繼承的val變量
    d.fun(); 	//編譯失敗,不知道調(diào)用從哪個(gè)類中繼承的fun函數(shù)
}

二師兄:解決的辦法有兩種,一種是在調(diào)用符之前加上父類限定符:

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

二師兄:但這里并沒有解決數(shù)據(jù)冗余的問題,因?yàn)镈中有B和C,而B和C各有一個(gè)虛表和一個(gè)int類型的成員變量,所以sizeof(D)的大小是32(x86_64架構(gòu),考慮到內(nèi)存對(duì)齊)。

二師兄:所幸在C++11引入了虛繼承(Virtual Inheritance)機(jī)制,從源頭上解決了這個(gè)問題:

#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
}

二師兄:此時(shí)在對(duì)象d中,只包含了一個(gè)val和兩個(gè)虛指針,成員變量的冗余問題得到解決。

面試官:一般我們認(rèn)為多態(tài)會(huì)影響性能,你舉得為什么影響性能?

二師兄:大多數(shù)人認(rèn)為,虛函數(shù)的調(diào)用會(huì)先通過虛指針跳到虛函數(shù)表,然后通過偏移確定函數(shù)真實(shí)地址,再跳轉(zhuǎn)到地址執(zhí)行,是間接調(diào)用導(dǎo)致了性能損失。

二師兄:但實(shí)際上無法內(nèi)聯(lián)才是虛函數(shù)性能低于正常函數(shù)的主要原因。由于多態(tài)是運(yùn)行時(shí)特征,在編譯時(shí)編譯器并不知道指針指向的函數(shù)地址,所以無法被內(nèi)聯(lián)。同時(shí)跳轉(zhuǎn)到特定地址執(zhí)行函數(shù)可能引發(fā)的L1 cache miss(空間局部性不好),這也會(huì)影響性能。

面試官:虛函數(shù)的調(diào)用一定是非內(nèi)聯(lián)的嗎?

二師兄:不是?,F(xiàn)代編譯器很聰明,如果編譯器能夠在編譯時(shí)推斷出真實(shí)的函數(shù),可能會(huì)直接內(nèi)聯(lián)這個(gè)虛函數(shù)。虛函數(shù)的調(diào)用是否內(nèi)聯(lián)取決于編譯器的實(shí)現(xiàn)和上下文。

面試官:你覺得多態(tài)在安全性上有沒有什么問題?

二師兄:的確是有的。當(dāng)我們把類中的虛函數(shù)定義為private的時(shí)候,雖然我們不能通過類的對(duì)象去訪問這個(gè)函數(shù),但我們知道這個(gè)函數(shù)就在虛函數(shù)表中,可以通過特殊的方法(上文中已經(jīng)給出示例)訪問它:

#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();  //編譯錯(cuò)誤
    using Fun = void(*)();
    size_t* virtual_point = (size_t*)&f;
    Fun* fun = (Fun*)*virtual_point;
    (*fun)();
}

面試官:好的,今天的面試到這里就結(jié)束了,請(qǐng)回去等通知吧。

總結(jié) 

到此這篇關(guān)于C++面試八股文之override和finial關(guān)鍵字有何作用的文章就介紹到這了,更多相關(guān)C++ override和finial關(guān)鍵字的作用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

长治市| 永清县| 南充市| 侯马市| 织金县| 马公市| 宜川县| 安多县| 彭州市| 乌恰县| 六枝特区| 阿拉善左旗| 宣汉县| 秭归县| 怀宁县| 定襄县| 玉田县| 吴堡县| 宜都市| 虞城县| 广南县| 海淀区| 澄城县| 宝坻区| 呼图壁县| 淮北市| 岱山县| 溆浦县| 驻马店市| 克什克腾旗| 荔浦县| 图片| 丹阳市| 漯河市| 曲松县| 社旗县| 巴里| 卢氏县| 延安市| 通榆县| 南华县|