欧美free性护士vide0shd,老熟女,一区二区三区,久久久久夜夜夜精品国产,久久久久久综合网天天,欧美成人护士h版

首頁綜合 正文
目錄

柚子快報邀請碼778899分享:開發(fā)語言 【C++】多態(tài)

柚子快報邀請碼778899分享:開發(fā)語言 【C++】多態(tài)

http://yzkb.51969.com/

多態(tài)的概念

了解多態(tài)之前,我們在現(xiàn)實生活一定經(jīng)歷過這倆個例子:

????????在購買火車票的時候,會根據(jù)你的類型來確定你的票價:

成人:全價學生:半價軍人:免費

? ? ? ? 而某外賣系統(tǒng)的紅包系統(tǒng),也會根據(jù)一定類型來確定紅包大?。?/p>

新人:大額紅包不經(jīng)常使用該外賣系統(tǒng)的人:中等額度經(jīng)常使用外面系統(tǒng)的人:小額紅包

從這些例子中,可以大致對多態(tài)有一定的理解:

多態(tài)的概念:通俗來講,就是多種形態(tài),具體點就是去完成某個行為,當不同的對象去完成時會產(chǎn)生出不同的狀態(tài)。

多態(tài)的定義以及實現(xiàn)

虛函數(shù)

class Person

{

public:

virtual void fare()

{

cout << "全價" << endl;

}

};

虛函數(shù):被virtual修飾的類成員函數(shù)被稱為虛函數(shù)。

虛函數(shù)的重寫

class Person

{

public:

virtual void fare()

{

cout << "全價" << endl;

}

};

class Student : public Person

{

public:

virtual void fare()

{

cout << "半價" << endl;

}

};

虛函數(shù)的重寫(覆蓋):派生類中有一個跟基類完全相同的虛函數(shù)(即派生類虛函數(shù)與基類虛函數(shù)的返回值相同,函數(shù)名字,參數(shù)列表完全相同),稱為子類的虛函數(shù)重寫了基類的虛函數(shù)。

class Person

{

public:

virtual void fare()

{

cout << "全價" << endl;

}

};

class Student : public Person

{

public:

void fare()

{

cout << "半價" << endl;

}

};

【注意】觀察上述代碼,在重寫基類虛函數(shù)的時候,派生類的虛函數(shù)在不加virtual關鍵字時,雖然也可也構成重寫(因為繼承后基類的虛函數(shù)被繼承下來,在派生類依舊保持虛函數(shù)屬性),但是這種寫法不是很規(guī)范,不建議這樣使用。

通過觀察代碼,前倆段代碼是通過對象類型來調用函數(shù),而后面?zhèn)z條代碼是通過看指向的對象,如果指向的對象是基類,則調用基類;如果指向的對象是派生類,則調用派生類。

虛函數(shù)重寫的倆個例外:

1.協(xié)變(基類與與派生類虛函數(shù)返回值類型不同)

派生類重寫基類虛函數(shù)時,與基類虛函數(shù)返回值類型不同。即基類虛函數(shù)返回基類對象的指針或者引用,派生類虛函數(shù)返回派生類對象的指針或者引用,稱為協(xié)變。

class A{};

class B : public A{};

class Person

{

public:

virtual A* fare()

{

cout << "全價" << endl;

}

};

class Student : public Person

{

public:

virtual B* fare()

{

cout << "半價" << endl;

}

};

2.析構函數(shù)的重寫(基類與派生類析構函數(shù)的名字不同)

如果基類的析構函數(shù)為虛函數(shù),此時派生類析構函數(shù)只要定義,無論是否加virtual關鍵字,都與基類的析構函數(shù)構成重寫,雖然基類與派生類析構函數(shù)名字不同。雖然函數(shù)名不相同,看起來違背了重寫的規(guī)則,但是這是不對的,這里可以理解為編譯器對析構函數(shù)的名稱做了特殊處理,編譯后析構函數(shù)的名稱被統(tǒng)一處理稱desructor。

觀察下面這段代碼:

class Person

{

public:

~Person()

{

cout << "~Person" << endl;

}

};

class Student : public Person

{

public:

~Student()

{

cout << "~Student" << endl;

}

};

int main()

{

Person* p = new Person;

delete p;

p = new Student;

delete p;

return 0;

}

這段代碼使用了為經(jīng)過處理的析構函數(shù),使用Person這個指針進行析構第一次p的時候,會調用Person這個析構函數(shù),而使用p析構第二次p的時候,還是只會調用Person這個析構函數(shù)。

?

class Person

{

public:

virtual ~Person()

{

cout << "~Person" << endl;

}

};

class Student : public Person

{

public:

virtual ~Student()

{

cout << "~Student" << endl;

}

};

int main()

{

Person* p = new Person;

delete p;

p = new Student;

delete p;

return 0;

}

????????將析構函數(shù)前面加上virtual,進行虛函數(shù)的重寫,可以將類析構函數(shù)都被處理為destructor這個統(tǒng)一的名字。

????????這里也是由于期望p->destructor()是一個多態(tài)調用,而不是普通調用。通過多態(tài)調用,可以將第二次的p在student析構一次,在Student析構一次,在Person析構一次。

? ? ? ? 這也是為什么將析構函數(shù)統(tǒng)一處理稱destructor這個名字的原因,為了保證函數(shù)名的統(tǒng)一。

class Person

{

public:

virtual ~Person()

{

cout << "~Person" << endl;

}

};

class Student : public Person

{

public:

virtual ~Student()

{

cout << "~Student" << endl;

}

};

int main()

{

Person* p1 = new Person;

delete p1;

Person* p2 = new Student;

delete p2;

return 0;

}

【注意】只有派生類student的析構函數(shù)重寫了person的析構函數(shù),當p1析構時會調用person的析構函數(shù),而p2析構的時候會調用person與student的析構函數(shù)。

【注意】派生類的析構可以不寫。

多態(tài)的構成條件

多態(tài)時在不同繼承關系的類對象,去調用同一個函數(shù),產(chǎn)生了不同的行為。

在繼承中構成多態(tài)的倆個條件:

1.必須通過基類的指針或者引用調用函數(shù)。

2.被調用的函數(shù)必須是虛函數(shù),且派生類必須對基類的虛函數(shù)進行重寫。

重寫虛函數(shù)的條件以及一些例外:

條件1:三同“函數(shù)名相同、參數(shù)相同、返回值相同”

條件2:必須是虛函數(shù)(virtual)

例外1:派生類的重寫虛函數(shù)可以不寫virtual,但是建議都加上。

例外2:協(xié)變的返回值可以不同,但是要求返回值必須是父子類關系的引用與指針。

例外3:析構函數(shù)可以名字不同。

【注意】:對于多態(tài)而言,不同的對象傳遞過去,會調用不同的函數(shù),多態(tài)調用看的是指向的對象,而普通類型調用看的是當前類型。

C++11特性:override與fianl

從前面的學習中了解到,C++對函數(shù)重寫的要求比較嚴格,但是可以由于各種原因導致函數(shù)名字母次序寫反而無法構成重載,而這種錯誤在編譯期間是不會報錯的,只有在程序運行時沒有達到預期的結果需要進行調試會得不償失。

C++11中提供了override和final倆個關鍵字,以便幫助用戶檢測是否重寫。

fianl:檢測虛函數(shù),表示該虛函數(shù)不能再被重寫。

class A

{

public:

virtual void Fun1() final

{

cout << "A" << endl;

}

};

class B : public class A

{

public:

virtual void Fun1()

{

cout << "B" << endl;

}

};

override:檢查派生類虛函數(shù)是否重寫了基類某個虛函數(shù),如果沒有重寫編譯錯誤。

class A

{

public:

virtual void Fun1()

{

cout << "A" << endl;

}

};

class B : public A

{

public:

virtual void Fun2() override

{

cout << "B" << endl;

}

};

如何設計一個不想被繼承的類?

倆種方法:

1.C++98的方法:將基類構造函數(shù)私有化(private)

class A

{

public:

private:

A()

{}

};

class B : public A

{

public:

B()

{}

};

此時會之間報錯:無法訪問 private 成員(在“A”類中聲明),私有構造函數(shù)在子類不可見,而派生類的構造函數(shù)需要調用基類的構造函數(shù)。

? ? ? ? 伴隨著報錯還會出現(xiàn)一系列的問題:該類如何進行初始化操作?可以使用一個函數(shù)來調用構造函數(shù)。

class A

{

public:

A Creatobj()

{

return A();

}

private:

A()

{}

};

?此時需要考慮的是,如果沒有初始化對象,那么該如何調用在函數(shù)來初始化對象呢?這是一個先有蛋還是先有雞的關系。

可以使用靜態(tài)函數(shù)來初始化創(chuàng)建對象:

class A

{

public:

static A Creatobj()

{

return A();

}

private:

A()

{}

};

int main()

{

A::Creatobj();

return 0;

}

可以通過這樣的方式來進行對對象的初始化。

2.C++11的方法:基類后面加一個fianl可以變成最終類。

在C++11中規(guī)定,如果一個類后面加關鍵字fianl會導致這個類變成最終類,而使其無法被繼承。

class A final

{

public:

};

class B : public A

{

public:

};

重載、覆蓋(重寫)、隱藏(重定義)的對比

多態(tài)的原理

虛函數(shù)表

觀察下面這段代碼:

class A

{

public:

virtual void Fun1()

{

cout << "A::Fun1()" << endl;

}

protected:

int _a;

};

int main()

{

A a;

cout << sizeof(a) << endl;

return 0;

}

在x86環(huán)境下,sizeof(a)的值為8.

????????這個答案可能有點不合常理,這是因為除了_a成員是int類型占了4個字節(jié),還多了一個_vfptr的指針占4個字節(jié),該指針被放在對象的前面(這是在vs環(huán)境下,有些平臺可能會放在對象的后面,這個跟平臺有關系),對象中的這個指針被稱為虛函數(shù)表指針(v代表virtual,f代表function)。

? ? ? ? 一個含有虛函數(shù)的類中都至少都會有一個虛函數(shù)表指針,這是因為虛函數(shù)的地址要被放在虛函數(shù)表中,虛函數(shù)表也簡稱虛表。

class A

{

public:

virtual void Fun1()

{

cout << "A::Fun1()" << endl;

}

virtual void Fun2()

{

cout << "A::Fun2()" << endl;

}

virtual void Fun3()

{

cout << "A::Fun3()" << endl;

}

protected:

int _a;

};

如果我們存放多個虛函數(shù),在這個虛函數(shù)指針所指向的表中就會出現(xiàn)三個地址。

class A

{

public:

virtual void Fun1()

{

cout << "A::Fun1()" << endl;

}

virtual void Fun2()

{

cout << "A::Fun2()" << endl;

}

void Fun3()

{

cout << "A::Fun3()" << endl;

}

protected:

int _a;

};

class B : public A

{

public:

virtual void Fun1()

{

cout << "B::Fun2()" << endl;

}

void Fun4()

{

cout << "B::Fun4()" << endl;

}

protected:

int _b;

};

int main()

{

A a;

B b;

return 0;

}

下面將通過測試來講解這段代碼:

1.派生類對象b中也有一個虛表函數(shù),b對象由倆部分組成,一部風是基類繼承下來的成員,虛表指針也就是存在這部分的,另一部分是自己的成員。

2.基類a對象和派生類b對象虛表是不一樣的,這里可以發(fā)現(xiàn)Fun1函數(shù)完成了重寫,所以b的虛表中存的是重寫的B::Fun1,所以虛函數(shù)的重寫也叫做覆蓋,覆蓋就是指虛表中虛函數(shù)的覆蓋。重寫是語法的叫法,覆蓋是原理層的叫法。

3.另外Fun2繼承下來后是虛函數(shù),所以被放進了虛表,F(xiàn)un3也被繼承了下來,但是由于沒有被關鍵字virtual修飾,不是虛函數(shù),所以不會放進虛表。

4.虛函數(shù)表本質是一存虛函數(shù)指針的指針數(shù)組,一般情況下這個數(shù)組最后面都會放一個nullptr。

class A

{

public:

virtual void func(int val = 1)

{

std::cout << "A->" << val << std::endl;

}

virtual void test()

{

func();

}

};

class B : public A

{

public:

void func(int val = 0)

{

std::cout << "B->" << val << std::endl;

}

};

int main(int argc, char* argv[])

{

B* p = new B;

p->test();

return 0;

}

測試這段代碼:其輸出結果是B->1.

? ? ? ? 這是因為虛函數(shù)的重寫的是虛函數(shù)的實現(xiàn),聲明式不會被改變的,所以在派生類可以不寫virtual,只需要存在三同即好,編譯器會在檢查之前,將基類的聲明域派生類的函數(shù)實現(xiàn)結合。

5.總結派生類的虛表生成:

先將基類中的虛表內(nèi)容拷貝一份到派生類虛表中如果派生類重寫了基類中的某個虛函數(shù),用派生類自己的虛函數(shù)覆蓋虛表中基類的虛函數(shù)派生類自己新增的虛函數(shù)按其在派生類中的聲明次序增加到派生類虛表的后面。

6.虛函數(shù)的重寫是接口繼承。

7.虛表是存在常量區(qū)的。

class A

{

public:

virtual void Fun1()

{

cout << "A::Fun1()" << endl;

}

protected:

int _a;

};

class B : public A

{

public:

virtual void Fun1()

{

cout << "B::Fun2()" << endl;

}

protected:

int _b;

};

int main()

{

int a = 0;

printf("棧區(qū):%p\n", &a);

static int b = 0;

printf("靜態(tài)區(qū):%p\n", &b);

int* p = new int;

printf("堆區(qū):%p\n", p);

const char* str = "hello";

printf("常量區(qū):%p\n", str);

A aa;

printf("虛表aa:%p\n", *((int*)&aa));

B bb;

printf("虛表bb:%p\n", *((int*)&bb));

return 0;

}

通過這段代碼的測試:

基本可以鎖定虛表存儲在常量區(qū)。

8.虛表里存的是虛函數(shù)指針,不是虛函數(shù),虛函數(shù)和普通函數(shù)一樣,都是存在代碼段里的。對象中存的也不是虛表,而是虛表指針。

多態(tài)的原理

之前的學習中我們知道,多態(tài)的條件有倆個:

1.必須通過基類的指針或者引用調用函數(shù)。

2.被調用的函數(shù)必須是虛函數(shù),且派生類必須對基類的虛函數(shù)進行重寫。

class A

{

public:

virtual void Fun1()

{

cout << "A::Fun1()" << endl;

}

protected:

int _a;

};

class B : public A

{

public:

virtual void Fun1()

{

cout << "B::Fun2()" << endl;

}

protected:

int _b;

};

int main()

{

A a;

A& pa = a;

pa.Fun1();

B b;

A& pb = b;

pb.Fun1();

return 0;

}

通過測試,我們可以知道不同對象去進行調用函數(shù)Fun1的時候會展現(xiàn)不同的形態(tài)。

這里有倆個問題:

1.為什么需要使用基類的指針才可以調用函數(shù),而不能是派生類指針或者引用。

? ? ? ? 是因為派生類指針或引用只能調用派生類的指針和引用,基類的指針或者引用不僅可以調用基類的指針或引用,也可以調用派生類的指針或引用。

2.為什么不能是父類的對象?

? ? ? ? 對象的切片與指針或者引用的切片不同,當派生類賦值給基類對象進行切片,對象會進行拷貝,但是不會拷貝虛表,假設如果拷貝了虛表,然后調用了派生類的成員函數(shù),但是當使用基類指針去指向該基類對象的時候,就也只能調用派生類的成員函數(shù),而不是基類的成員函數(shù)。

class A

{

public:

virtual void Fun1()

{

cout << "A::Fun1()" << endl;

}

protected:

int _a;

};

class B : public A

{

public:

virtual void Fun1()

{

cout << "B::Fun1()" << endl;

}

protected:

int _b;

};

int main()

{

B b;

A& pb = b;

pb.Fun1();

b.Fun1();

return 0;

}

我們通過反匯編的角度,分析使用基類引用與對象調用函數(shù)的不同:

這是基類引用時的匯編代碼。

pb中存的時基類的指針,將pb移動到eax中。

[eax]就是取eax值指向的內(nèi)容,這里相當于把b對象頭4個字節(jié)(虛表指針)移動到了edx

[edx]就是取edx值指向的內(nèi)容,這里相當于把虛表中的頭4個字節(jié)存的虛函數(shù)指針移動到eax

call eax中存虛函數(shù)的指針。這里可以看出滿足多態(tài)的調用,不是在編譯時確定的,是運行起來以后對象里面取到的。

這里不滿足多態(tài)的條件,所以這里是普通函數(shù)的調用轉換成地址時,是在編譯時已經(jīng)從符號表確認了函數(shù)的地址,直接call地址。

? ? ? ? 通過上面的了解,看出滿足多態(tài)以后的函數(shù)調用,不是在編譯時確定的,是運行起來以后到對象里面取棧的,不滿足多態(tài)的函數(shù)調用時編譯時確認好的。

動態(tài)綁定與靜態(tài)綁定

在上述例子中總結:

1.靜態(tài)綁定:靜態(tài)綁定又稱為前期綁定(早綁定),在程序編譯期間確定了程序的行為,也稱為靜態(tài)多態(tài),比如函數(shù)重載。

2.動態(tài)綁定:動態(tài)綁定又稱為后期綁定(晚綁定),是在程序運行期間,根據(jù)具體拿到的類型確定程序的具體行為,調用具體的函數(shù),也稱為動態(tài)多態(tài)。

單繼承和多繼承關系的虛函數(shù)表

單繼承中的虛函數(shù)表

class A

{

public:

virtual void Fun1()

{

cout << "A::Fun1()" << endl;

}

virtual void Fun2()

{

cout << "A::Fun2()" << endl;

}

protected:

int _a;

};

class B : public A

{

public:

virtual void Fun1()

{

cout << "B::Fun1()" << endl;

}

virtual void Fun3()

{

cout << "B::Fun3()" << endl;

}

virtual void Fun4()

{

cout << "B::Fun4()" << endl;

}

protected:

int _b;

};

使用這段代碼進行測試的時候,在編譯的監(jiān)視窗口看不見fun3和fun4。

這里是編譯器的監(jiān)視窗口故意隱藏了這倆個函數(shù),也可以認為這是一個bug。

// 打印函數(shù)指針數(shù)組

// void PrintVFT(FUNC_PTR table[])

void PrintVFT(FUNC_PTR* table)

{

for (size_t i = 0; table[i] != nullptr; i++)

{

printf("[%d]:%p->", i, table[i]);

FUNC_PTR f = table[i];

f();

}

printf("\n");

}

int main()

{

A a;

B b;

int vft1 = *((int*)&a);

PrintVFT((FUNC_PTR*)vft1);

int vft2 = *((int*)&b);

PrintVFT((FUNC_PTR*)vft2);

return 0;

}

這里提供一種思路:

取出a,b對象的頭4個字節(jié),就是虛表的指針,前面講解了虛函數(shù)的本質是一個存虛函數(shù)指針的指針數(shù)組,這個數(shù)組最后面放了一個nullptr。

1.先取a的地址,強轉成一個int*的指針。

2.再解引用取值,就取到了a對象的頭4個字節(jié)的指針,這個值就是指向虛表的指針。

3.再強轉成FUNC_PTR*,因為虛表就是一個存FUNC_PTR類型(虛函數(shù)指針類型)的數(shù)組。

4.虛表指針傳遞給PrintVFT進行打印虛表。

5.需要說明的是這個打印虛表的代碼經(jīng)常會崩潰,因為編譯器有時對虛表的處理不干凈,虛表最后沒有放nullptr的話,會導致越界,這是編譯器的問題。只需要清理解決方案即可。

多繼承中的虛函數(shù)表

class Base1 {

public:

virtual void func1() { cout << "Base1::func1" << endl; }

virtual void func2() { cout << "Base1::func2" << endl; }

private:

int b1;

};

class Base2 {

public:

virtual void func1() { cout << "Base2::func1" << endl; }

virtual void func2() { cout << "Base2::func2" << endl; }

private:

int b2;

};

class Derive : public Base1, public Base2 {

public:

virtual void func1() { cout << "Derive::func1" << endl; }

virtual void func3() { cout << "Derive::func3" << endl; }

private:

int d1;

};

typedef void(*VFPTR) ();

void PrintVTable(VFPTR vTable[])

{

cout << " 虛表地址>" << vTable << endl;

for (int i = 0; vTable[i] != nullptr; ++i)

{

printf(" 第%d個虛函數(shù)地址 :0X%x,->", i, vTable[i]);

VFPTR f = vTable[i];

f();

}

cout << endl;

}

int main()

{

Derive d;

VFPTR* vTableb1 = (VFPTR*)(*(int*)&d);

PrintVTable(vTableb1);

VFPTR* vTableb2 = (VFPTR*)(*(int*)((char*)&d + sizeof(Base1)));

PrintVTable(vTableb2);

return 0;

}

?多繼承派生類的未重寫的虛函數(shù)放在第一個繼承基類部分的虛函數(shù)表中。

菱形繼承與虛擬菱形繼承

菱形繼承

class A

{

public:

virtual void Func1()

{

cout << "A::Func1" << endl;

}

public:

int _a;

};

class B : public A

{

public:

virtual void Fun1()

{

cout << "B::Func1" << endl;

}

public:

int _b;

};

class C : public A

{

public:

virtual void Fun1()

{

cout << "B::Func1" << endl;

}

public:

int _c;

};

class D : public B, public C

{

public:

virtual void Fun1()

{

cout << "B::Func1" << endl;

}

public:

int _d;

};

int main()

{

D d;

d.B::_a = 1;

d.C::_a = 2;

d._b = 3;

d._c = 4;

d._d = 5;

return 0;

}

這里需要注意在d的所在地址第一塊區(qū)域是類B的所在區(qū)域,里面包含了虛表的地址,以及數(shù)據(jù)B::_a和_b。第二塊區(qū)域是類C的所在區(qū)域,里面包含了虛表的地址,以及數(shù)據(jù)C::_a和_c。

菱形虛擬繼承

class A

{

public:

virtual void Func1()

{

cout << "A::Func1" << endl;

}

public:

int _a;

};

class B : virtual public A

{

public:

virtual void Fun1()

{

cout << "B::Func1" << endl;

}

public:

int _b;

};

class C : virtual public A

{

public:

virtual void Fun1()

{

cout << "B::Func1" << endl;

}

public:

int _c;

};

class D : public B, public C

{

public:

virtual void Fun1()

{

cout << "B::Func1" << endl;

}

public:

int _d;

};

int main()

{

D d;

d._a = 1;

d._b = 3;

d._c = 4;

d._d = 5;

return 0;

}

????????實際中不建議設計出菱形繼承及菱形虛擬繼承,一方面太復雜容易出現(xiàn)問題,另一方面這樣的模型,訪問基類成員有一定的性能損耗。

? ? ? ? 所以菱形繼承、菱形虛擬繼承一般實際中很少使用。?

抽象類

概念

? ? ? ? 在虛函數(shù)的后面寫上=0,則這個函數(shù)變成純虛函數(shù)。包含純虛函數(shù)的類叫做抽象類(也叫接口類),抽象類不能實例化出對象。派生類繼承之后也不能實例化出對象,只能重寫純虛函數(shù),派生類才能實例化出對象。純虛函數(shù)規(guī)范了派生類必須重寫,另外純虛函數(shù)更體現(xiàn)處理接口繼承。

class Car

{

public:

inline virtual void Drive() = 0;

};

class Benz :public Car

{

public:

inline virtual void Drive()

{

cout << "Benz-舒適" << endl;

}

};

class BMW :public Car

{

public:

inline virtual void Drive()

{

cout << "BMW-操控" << endl;

}

};

class BYD :public Car

{

public:

inline virtual void Drive()

{

cout << "BYD-build year dream" << endl;

}

};

void Func(Car* p)

{

p->Drive();

}

int main()

{

Func(new Benz);

Func(new BMW);

Func(new BYD);

return 0;

}

這個類在現(xiàn)實世界沒有對應的實體,其用途可以是作為指針或者引用使用。

接口繼承和實現(xiàn)繼承

? ? ? ? 普通函數(shù)的繼承是一種實現(xiàn)繼承,派生類繼承了基類函數(shù),可以使用函數(shù),繼承的是函數(shù)的實現(xiàn)。

? ? ? ? 虛函數(shù)的繼承是一種接口繼承,派生類繼承的是基類函數(shù)的接口,目的是為了重寫,達成多態(tài),繼承的是接口。所以如果不實現(xiàn)多態(tài),就不需要把函數(shù)定義為虛函數(shù)。

面試相關題

1.

什么是多態(tài)?

答:多態(tài)分為靜態(tài)多態(tài):函數(shù)重載;和動態(tài)多態(tài):繼承中的虛函數(shù)重寫+基類指針引用。

2.

什么是重載、重寫

(

覆蓋

)

、重定義

(

隱藏

)

?

答:參考本節(jié)課件內(nèi)容

3.

多態(tài)的實現(xiàn)原理?

答:靜態(tài)多態(tài):函數(shù)名修飾規(guī)則;動態(tài)多態(tài):虛函數(shù)表

4. inline

函數(shù)可以是虛函數(shù)嗎?

答:可以,不過編譯器就忽略

inline

屬性,這個函數(shù)就不再是inline,因為虛函數(shù)要放到虛表中去。

5.

靜態(tài)成員可以是虛函數(shù)嗎?

答:不能,因為靜態(tài)成員函數(shù)沒有

this

指針,使用類型

::

成員函數(shù)的調用方式無法訪問虛函數(shù)表,所以靜態(tài)成員函數(shù)無法放進虛函數(shù)表。

6.

構造函數(shù)可以是虛函數(shù)嗎?

答:不能,因為對象中的虛函數(shù)表指針是在構造函數(shù)初始化列表 階段才初始化的。

7.

析構函數(shù)可以是虛函數(shù)嗎?什么場景下析構函數(shù)是虛函數(shù)?

答:可以,并且最好把基類的析 構函數(shù)定義成虛函數(shù)。參考本節(jié)課件內(nèi)容

8.

對象訪問普通函數(shù)快還是虛函數(shù)更快?

答:首先如果是普通對象,是一樣快的。如果是指針 對象或者是引用對象,則調用的普通函數(shù)快,因為構成多態(tài),運行時調用虛函數(shù)需要到虛函 數(shù)表中去查找。

9.

虛函數(shù)表是在什么階段生成的,存在哪的?

答:虛函數(shù)表是在編譯階段就生成的,一般情況 下存在代碼段(

常量區(qū)

)

的。

10. C++

菱形繼承的問題?虛繼承的原理?

答:參考繼承課件。注意這里不要把虛函數(shù)表和虛基表搞混了。

11.

什么是抽象類?抽象類的作用?

答:參考(3.

抽象類)。抽象類強制重寫了虛函數(shù),另外抽象類體現(xiàn)出了接口繼承關系。

柚子快報邀請碼778899分享:開發(fā)語言 【C++】多態(tài)

http://yzkb.51969.com/

相關閱讀

評論可見,查看隱藏內(nèi)容

本文內(nèi)容根據(jù)網(wǎng)絡資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。

轉載請注明,如有侵權,聯(lián)系刪除。

本文鏈接:http://gantiao.com.cn/post/19302795.html

發(fā)布評論

您暫未設置收款碼

請在主題配置——文章設置里上傳

掃描二維碼手機訪問

文章目錄