linux c/c++面試知識(shí)點(diǎn)整理(三)

21、類成員函數(shù)的重載、覆蓋和隱藏的區(qū)別

重載即為函數(shù)重載,重載的特征:
? ? ? ?(1)相同的范圍,也就是在同一個(gè)類中;
? ? ? ?(2)函數(shù)名字相同;
? ? ? ?(3)參數(shù)不同;
? ? ? ?(4)virtual關(guān)鍵字無(wú)影響;
demo如下:

#include <stdio.h>
class CPerson
{
public:
    CPerson():height(170),weight(120)
    {
        printf("無(wú)參構(gòu)造 身高:%d, 體重: %d\n", height, weight);
    }
    //構(gòu)造函數(shù)重載
    CPerson(int p_iHeight, int p_iWeight):height(p_iHeight), weight(p_iWeight)
    {
        printf("有參構(gòu)造 身高:%d, 體重: %d\n", height, weight);
    }
    ~CPerson(){}

private:
    int height;
    const int weight;

};

覆蓋也叫多態(tài),是指派生類函數(shù)覆蓋基類函數(shù),覆蓋的特征:
? ? ? ?(1)不同的范圍,即函數(shù)分別位于派生類和基類;
? ? ? ?(2)函數(shù)名字相同;
? ? ? ?(3)參數(shù)相同;
? ? ? ?(4)基類函數(shù)必須有virtual關(guān)鍵字;
demo見(jiàn)我的另一篇文章:
linux c/c++面試知識(shí)點(diǎn)整理(一)

隱藏是指派生類的函數(shù)屏蔽了與其同名的基類函數(shù),特征如下:
? ? ? ?(1)如果派生類的函數(shù)與基類的函數(shù)同名,但是參數(shù)不同,此時(shí)不論有沒(méi)有virtual關(guān)鍵字,基類的函數(shù)都將被隱藏;
? ? ? ?(2)如果派生類的函數(shù)與基類的函數(shù)同名,參數(shù)也相同,但是基類函數(shù)沒(méi)有virtual關(guān)鍵字,此時(shí),基類的函數(shù)將被隱藏;

總結(jié):函數(shù)名相同,參數(shù)也相同的情況下,如果基類函數(shù)有virtual關(guān)鍵字,則是多態(tài),否則就是隱藏;函數(shù)名相同,參數(shù)不同的情況下,如果函數(shù)位于同一個(gè)類中,則是重載,否則就是隱藏。

22、main主函數(shù)執(zhí)行完畢后,是否可能會(huì)再執(zhí)行一段代碼

? ? ? ?atexit函數(shù),是注冊(cè)終止函數(shù),即main執(zhí)行結(jié)束后調(diào)用的函數(shù),注冊(cè)以后函數(shù)將由exit函數(shù)自動(dòng)調(diào)用,其中atexit注冊(cè)的函數(shù)類型應(yīng)該是不接受任何參數(shù)的void函數(shù),exit調(diào)用這些注冊(cè)函數(shù)的順序與它們登記時(shí)候的順序相反。
? ? ? ?另外,atexit函數(shù)是包含在stdlib.h庫(kù)中。
demo如下:

#include <stdlib.h>  //atexit
#include <unistd.h>  //sleep

void test1()
{
    printf("hello test1\n");
}

void test2()
{
    printf("hello test2\n");
}

void test3()
{
    printf("hello test3\n");
}

int main()
{
    atexit(test1);
    atexit(test2);
    atexit(test3);
    sleep(5);
    printf("hello main\n");

    return 0;
}

執(zhí)行結(jié)果如下:
[root@kingdom ~]# ./a.out
hello main
hello test3
hello test2
hello test1

23、代碼中特殊的注釋技術(shù)-TODO, FIXME 和 XXX

? ? ? ?TODO:如果代碼中有該標(biāo)識(shí),說(shuō)明在標(biāo)識(shí)處有功能代碼待編寫, 待實(shí)現(xiàn)的功能在說(shuō)明中會(huì)簡(jiǎn)略說(shuō)明;
? ? ? ?FIXME:如果代碼中有該標(biāo)識(shí),說(shuō)明標(biāo)識(shí)處代碼需要修正,甚至代碼是錯(cuò)誤的,不能工作,需要修復(fù),如何修正會(huì)在說(shuō)明中簡(jiǎn)略說(shuō)明;
? ? ? ?XXX:如果代碼中有該標(biāo)識(shí),說(shuō)明標(biāo)識(shí)處代碼雖然實(shí)現(xiàn)了功能,但是實(shí)現(xiàn)的方法有待商榷,希望將來(lái)能改進(jìn),要改進(jìn)的地方會(huì)在說(shuō)明中簡(jiǎn)略說(shuō)明。

24、代碼中連用兩個(gè)感嘆號(hào)

? ? ? ?c代碼中連用兩個(gè)感嘆號(hào)表示非非,如果是0,那么還是0,如果原來(lái)是非0,則變?yōu)?.

25、對(duì)友元的淺析

采用類的機(jī)制后實(shí)現(xiàn)了數(shù)據(jù)的隱藏與封裝,類的數(shù)據(jù)成員一般定義為私有成員,成員函數(shù)一般定義為公有的,依此提供類與外界間的通信接口。但是,有時(shí)需要定義一些函數(shù),這些函數(shù)不是類的一部分,但又需要頻繁地訪問(wèn)類的數(shù)據(jù)成員,這時(shí)可以將這些函數(shù)定義為該函數(shù)的友元函數(shù)。除了友元函數(shù)外,還有友元類,兩者統(tǒng)稱為友元。友元的作用是提高了程序的運(yùn)行效率(即減少了類型檢查和安全性檢查等都需要時(shí)間開(kāi)銷),但它破壞了類的封裝性和隱藏性,使得非成員函數(shù)可以訪問(wèn)類的私有成員和保護(hù)成員,相當(dāng)于在墻上打了一個(gè)洞一樣。

  • 友元函數(shù)
    ? ? ? ?友元函數(shù)是可以直接訪問(wèn)類的私有成員的非成員函數(shù)。它是定義在類外的普通函數(shù),它不屬于任何類,但需要在類的定義中加以聲明,聲明時(shí)只需在友元的名稱前加上關(guān)鍵字friend,demo如下:
#include <stdio.h>
class CPerson
{
public:
    CPerson()
    {
        height = 170;
    }
    friend void printHeight();
private:
    int height;
};

void printHeight()
{
    CPerson person;
    printf("身高:%d\n", person.height);
}

int main()
{
    printHeight();
    return 0;
}

? ? ? ?友元函數(shù)的聲明可以放在類的私有部分,也可以放在公有部分,它們是沒(méi)有區(qū)別的,都說(shuō)明是該類的一個(gè)友元函數(shù)。
? ? ? ?一個(gè)函數(shù)可以是多個(gè)類的友元函數(shù),只需要在各個(gè)類中分別聲明。
? ? ? ?友元函數(shù)的調(diào)用與一般函數(shù)的調(diào)用方式和原理一致。

  • 友元類
    ? ? ? ?友元類的所有成員函數(shù)都是另一個(gè)類的友元函數(shù),都可以訪問(wèn)另一個(gè)類中的隱藏信息(包括私有成員和保護(hù)成員)。
    ? ? ? ?當(dāng)希望一個(gè)類可以存取另一個(gè)類的私有成員時(shí),可以將該類聲明為另一類的友元類。定義友元類的語(yǔ)句demo如下:
       friend class 類名;
       例如,以下語(yǔ)句說(shuō)明類B是類A的友元類:
       class A
       {
              …
       public:
              friend class B;
              …
       };

? ? ? ?經(jīng)過(guò)以上說(shuō)明后,類B的所有成員函數(shù)都是類A的友元函數(shù),能存取類A的私有成員和保護(hù)成員。

使用友元類時(shí)注意:

  • 友元關(guān)系不能被繼承。
  • 友元關(guān)系是單向的,不具有交換性。若類B是類A的友元,類A不一定是類B的友元,要看在類中是否有相應(yīng)的聲明。
  • 友元關(guān)系不具有傳遞性。若類B是類A的友元,類C是B的友元,類C不一定是類A的友元,同樣要看類中是否有相應(yīng)的申明

26、多態(tài)的內(nèi)部邏輯

? ? ? ?(1)從包含虛函數(shù)的類派生一個(gè)類時(shí),編譯器就為該類創(chuàng)建一個(gè)VTABLE,其每一個(gè)表項(xiàng)是該類的虛函數(shù)地址;
? ? ? ?(2)在定義該派生類對(duì)象時(shí),先調(diào)用其基類的構(gòu)造函數(shù),然后再初始化VPTR,最后再調(diào)用派生類的構(gòu)造函數(shù)(從二進(jìn)制的視野來(lái)看,所謂基類子類是一個(gè)大結(jié)構(gòu)體,其中this指針開(kāi)頭的四個(gè)字節(jié)存放虛函數(shù)表頭指針,執(zhí)行子類的構(gòu)造函數(shù)的時(shí)候,首先調(diào)用基類構(gòu)造函數(shù),this指針作為參數(shù),在基類的構(gòu)造函數(shù)中填入基類的vptr,然后回到子類的構(gòu)造函數(shù),填入子類的vptr,覆蓋基類填入的vptr,如此一來(lái)完成vptr的初始化)
? ? ? ?(3)在實(shí)現(xiàn)動(dòng)態(tài)綁定,不能直接采用類對(duì)象,而一定要采用指針或者引用,因?yàn)椴捎妙悓?duì)象傳值方式,有臨時(shí)基類對(duì)象的產(chǎn)生,而采用指針,則是通過(guò)指針來(lái)訪問(wèn)外部的派生類對(duì)象的vptr來(lái)達(dá)到訪問(wèn)派生類虛函數(shù)的結(jié)果。

27、虛析構(gòu)函數(shù)的作用

? ? ? ?只有當(dāng)一個(gè)類被用作基類時(shí)才需要使用虛析構(gòu)函數(shù),這樣做的作用是當(dāng)一個(gè)基類的指針刪除派生類的對(duì)象時(shí),能確保派生類的析構(gòu)函數(shù)會(huì)被調(diào)用。因?yàn)榫幾g器它只知道基類指針,調(diào)用基類析構(gòu),并不會(huì)主動(dòng)去調(diào)用派生類的析構(gòu)函數(shù),所以基類析構(gòu)函數(shù)需為虛析構(gòu)函數(shù),這就相當(dāng)于析構(gòu)函數(shù)的多態(tài)。

28、#ifndef 和 #pragma once的區(qū)別

? ? ? ?#ifndef是手動(dòng)定義宏名來(lái)避免沖突,但#pragma once是編譯器提供保證。
? ? ? ?#ifndef是依賴于宏的名字不能起沖突,可以保證同一個(gè)文件不會(huì)被包含多次,但缺點(diǎn)是如果不同頭文件的宏名不小心撞車了,可能就會(huì)導(dǎo)致頭文件命名存在,但編譯器卻報(bào)找不到聲明的情況。
? ? ? ?#pragma once由編譯器自動(dòng)提供保障,同一個(gè)物理上的文件不會(huì)被包含多次,就是內(nèi)容相同,但只要是兩個(gè)文件,都會(huì)分別包含,但如果一個(gè)頭文件被拷貝了多份,這種方法就不能保證文件不被重復(fù)包含。
? ? ? ?#ifndef語(yǔ)法移植性好,#pragma once可以避免名字沖突。

29、__declspec(dllexcept) 和 __declspec(dllimport)

? ? ? ?#ifdef ASIMOVLIB_EXPORTS
? ? ? ?#define ASIMOVLIB_API __declspec(dllexport)
? ? ? ?#else
? ? ? ?#define ASIMOVLIB_API __declspec(dllimport)
? ? ? ?#endif
? ? ? ?_declspec(dllexport):
? ? ? ?聲明一個(gè)導(dǎo)出函數(shù),是說(shuō)這個(gè)函數(shù)要從本DLL導(dǎo)出。我要給別人用。一般用于dll中省掉在DEF文件中手工定義導(dǎo)出哪些函數(shù)的一個(gè)方法。當(dāng)然,如果你的DLL里全是C++的類的話, 你無(wú)法在DEF里指定導(dǎo)出的函數(shù),只能用dllexport導(dǎo)出類。
? ? ? ?__declspec(dllimport):
? ? ? ?聲明一個(gè)導(dǎo)入函數(shù),是說(shuō)這個(gè)函數(shù)是從別的dll導(dǎo)入,我要用,一般用于使用某個(gè)dll的exe中。
? ? ? ?不使用__declspec(dllimport)也能正確編譯代碼,但使用__declspec(dllimport)使編譯器可以生成更好的代碼。編譯器之所以能生成更好的代碼,是因?yàn)樗梢源_定函數(shù)是否存在于DLL中,這使得編譯器可以生成跳過(guò)間接尋址級(jí)別的代碼,而這些代碼通常會(huì)出現(xiàn)在跨DLL邊界的函數(shù)調(diào)用中。但是,必須使用__declspec(import)才能導(dǎo)入DLL中使用的變量。

30、explicit關(guān)鍵字的作用

? ? ? ?explicit用來(lái)防止由構(gòu)造函數(shù)定義的隱式轉(zhuǎn)換,比如:class Base base=10;即Base類只有一個(gè)int類型的變量,explicit使用了以后,就不允許這樣寫。
? ? ? ?被聲明為explicit的構(gòu)造函數(shù)通常比非explicit的構(gòu)造函數(shù)更受歡迎,因?yàn)樗鼈兘咕幾g器執(zhí)行非預(yù)期的類型轉(zhuǎn)換。除非我有個(gè)好理由允許構(gòu)造函數(shù)被用于隱式類型轉(zhuǎn)換,否則我們應(yīng)該把它聲明為explicit。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容