欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++多態(tài)的全面講解

 更新時(shí)間:2022年06月06日 09:20:42   作者:林慢慢腦瓜子嗡嗡的  
多態(tài)按字面的意思就是多種形態(tài)。當(dāng)類之間存在層次結(jié)構(gòu),并且類之間是通過繼承關(guān)聯(lián)時(shí),就會(huì)用到多態(tài)。C++?多態(tài)意味著調(diào)用成員函數(shù)時(shí),會(huì)根據(jù)調(diào)用函數(shù)的對(duì)象的類型來執(zhí)行不同的函數(shù)

1.多態(tài)的定義和實(shí)現(xiàn)

多態(tài)的淺層理解

多態(tài),即多種形態(tài),也就是說,不同的對(duì)象在完成某個(gè)行為時(shí)會(huì)產(chǎn)生不同的狀態(tài)。

舉個(gè)例子,買火車票時(shí),普通人正常買票,學(xué)生半價(jià)買票,軍人優(yōu)先買票。

在C++中,多態(tài)就是對(duì)于同一個(gè)函數(shù),當(dāng)調(diào)用的對(duì)象不同,他的操作也不同。

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

多態(tài)是繼承體系中的一個(gè)行為,如果要在繼承體系中構(gòu)成多態(tài),需要滿足兩個(gè)條件:

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

2. 被調(diào)用的函數(shù)必須是虛函數(shù),并且派生類必須要對(duì)繼承的基類的虛函數(shù)進(jìn)行重寫

解釋1:因?yàn)樽宇惡透割惖奶摫砀髯砸环荩热裟軌蛲ㄟ^對(duì)象傳遞的方式同時(shí)傳遞虛表的話,那么父類就可能拿到子類的虛表,不合理。

解釋2:有虛函數(shù)就有虛函數(shù)表,對(duì)象當(dāng)中就會(huì)存放一個(gè)虛基表指針,通過虛基表指針指向的內(nèi)容來訪問對(duì)應(yīng)的函數(shù)。若子類沒有重寫父類的虛函數(shù)內(nèi)容,則子類也會(huì)調(diào)用父類的函數(shù)。

2.虛函數(shù)

虛函數(shù)就是被virtual修飾的類成員函數(shù)(這里的virtual和虛繼承的virtual雖然是同一個(gè)關(guān)鍵字,但是作用不一樣)

class Person
{
public:
	virtual void func()
	{
		cout << "普通人->正常買票" << endl;
	}
};

虛函數(shù)的重寫規(guī)則

虛函數(shù),即被virtual關(guān)鍵字重寫的類成員函數(shù)。

重寫(覆蓋):派生類中有一個(gè)跟基類中完全相同的虛函數(shù)(三同:即派生類虛函數(shù)與基類虛函數(shù)的返回值類型、函數(shù)名字、參數(shù)列表完全相同,也有例外!),這樣則稱子類重寫了父類的虛函數(shù)。

示例代碼如下:

class Person
{
public:
	virtual void func()
	{
		cout << "普通人->正常買票" << endl;
	}
};
class Student : public Person
{
public:
	//子類必須重寫父類的虛函數(shù)
	virtual void func()
	{
		cout << "學(xué)生->半價(jià)買票" << endl;
	}
};
//必須是父類的指針或引用去調(diào)用虛函數(shù)
//這里的參數(shù)類型不能是對(duì)象,否則是一份臨時(shí)拷貝,則無法構(gòu)成多態(tài)
void F(Person& ps)
{
	ps.func();
}
int main()
{
	Person ps;
	Student st;
	F(ps);
	F(st);
	return 0;
}

筆試選擇題??键c(diǎn)(選自Effective C++):

如果不滿足虛函數(shù)重寫的條件,例如參數(shù)不同則會(huì)變成重定義。

思考如下代碼輸出結(jié)果:

#include <iostream>
class Base{
public:
    virtual void Show(int n = 10)const{    //提供缺省參數(shù)值
        std::cout << "Base:" << n << std::endl;
    }
};
class Base1 : public Base{
public:
    virtual void Show(int n = 20)const{     //重新定義繼承而來的缺省參數(shù)值
        std::cout << "Base1:" << n << std::endl;
    }
};
int main(){
    Base* p1 = new Base1;        
    p1->Show();           
    return 0;
}

輸出的是Base1:10,

因?yàn)槿绻宇愔貙懥巳笔≈?,此時(shí)的子類的缺省值是無效的,使用的還是父類的缺省值。原因是因?yàn)槎鄳B(tài)是動(dòng)態(tài)綁定,而缺省值是靜態(tài)綁定。對(duì)于P1,他的靜態(tài)類型也就是這個(gè)指針的類型是Base,所以這里的缺省值是Base的缺省值,而動(dòng)態(tài)類型也就是指向的對(duì)象是Base1,所以這里調(diào)用的虛函數(shù)則是Base1中的虛函數(shù),所以這里就是Base1中的虛函數(shù),Base中的缺省值,也就是Base1:10。

簡(jiǎn)單概括就是:虛函數(shù)的重寫只重寫函數(shù)實(shí)現(xiàn),不重寫缺省值。

虛函數(shù)重寫條件的兩個(gè)例外

1.協(xié)變(返回值不同)

當(dāng)基類和派生類的返回值類型不同時(shí),如果基類對(duì)象返回基類對(duì)象的引用或者指針,派生類對(duì)象也返回的是派生類對(duì)象的引用或者指針時(shí),就會(huì)引起協(xié)變。協(xié)變也能完成虛函數(shù)的重寫

例1:指針

class A {};
class B :public A {};
class Person
{
public:
	virtual A* func()
	{
		cout << "virtual A* func()" << endl;
		return new A;
	}
};
class Student : public Person
{
public:
	virtual B* func()
	{
		cout << "virtual B* func()" << endl;
		return new B;
	}
};

例2:引用

class Human
{
public:
	virtual Human& print()
	{
		cout << "i am a human" << endl;
		return *this;
	}
};
class Student : public Human
{
public:
	virtual Student& print()
	{
		cout << "i am a student" << endl;
		return *this;
	}
};

2.析構(gòu)函數(shù)的重寫(函數(shù)名不同)

析構(gòu)函數(shù)雖然函數(shù)名不同,但是也能構(gòu)成重寫,因?yàn)檎驹诰幾g器的視角,他所調(diào)用的析構(gòu)函數(shù)名稱都叫做destructor

為什么編譯器要通過這種方式讓析構(gòu)函數(shù)也能構(gòu)成重寫呢?

假設(shè)我用一個(gè)基類指針或者引用指向派生類對(duì)象,如果不構(gòu)成多態(tài)會(huì)怎樣?

class Human
{
public:
	~Human()
	{
		cout << "~Human()" << endl;
	}
};
class Student : public Human
{
public:
	~Student()
	{
		cout << "~Student()" << endl;
	}
};
int main()
{
	Human* h = new Student;
	delete h;
	return 0;
}

上述代碼只會(huì)調(diào)用Human的析構(gòu)函數(shù),即如果不構(gòu)成多態(tài),那么指針是什么類型的,就會(huì)調(diào)用什么類型的析構(gòu)函數(shù),這也就導(dǎo)致了一種情況,如果派生類的析構(gòu)函數(shù)中有資源釋放,而這里卻沒有釋放掉那些資源,就會(huì)導(dǎo)致內(nèi)存泄漏的問題。

所以為了防止這種情況,必須要將析構(gòu)函數(shù)定義為虛函數(shù)。這也就是編譯器將析構(gòu)函數(shù)重命名為destructor的原因

class Human
{
public:
	virtual ~Human()
	{
		cout << "~Human()" << endl;
	}
};
class Student : public Human
{
public:
	virtual ~Student()
	{
		cout << "~Student()" << endl;
	}
};
int main()
{
	Human* h = new Student;
	delete h;
	return 0;
}

3.C++11 override 和 final

override

override關(guān)鍵字是用來檢測(cè)派生類虛函數(shù)是否構(gòu)成重寫的關(guān)鍵字。

如基類虛函數(shù)沒有virtual或者派生類虛函數(shù)名拼錯(cuò)等問題,這些問題不會(huì)被編譯器檢查出來,發(fā)生錯(cuò)誤時(shí)也很難一下子鎖定,所以C++增添了override這一層保險(xiǎn),當(dāng)修飾的虛函數(shù)不構(gòu)成重寫時(shí)就會(huì)編譯錯(cuò)誤。

class A
{
public:
	virtual void func() {}
};
class B : public A
{
public:
	//未重寫則報(bào)錯(cuò)
	virtual void func() override {};
};

final

使用final修飾的虛函數(shù)不能被重寫。

如果某一個(gè)虛函數(shù)不想被派生類重寫,就可以用final來修飾這個(gè)虛函數(shù)

class Human
{
public:
	virtual void print() final
	{
		cout << "i am a human" << endl;
	}
};
class Student : public Human
{
public:
	virtual void print()
	{
		cout << "i am a student" << endl;
	}
};

重載對(duì)比重定義(隱藏)與重寫(覆蓋)

4.抽象類

虛函數(shù)后面加上=0就是純虛函數(shù),包含純虛函數(shù)的類即為抽象類(接口類)。抽象類不能實(shí)例化出對(duì)象,派生類繼承抽象類后若沒有重寫純虛函數(shù)那么仍為抽象類,亦不能實(shí)例化出對(duì)象。純虛函數(shù)規(guī)范了派生類必須重寫虛函數(shù),并且更加體現(xiàn)出了接口繼承。

抽象類就像是一個(gè)藍(lán)圖,為派生類描述好一個(gè)大概的架構(gòu),派生類必須實(shí)現(xiàn)完這些架構(gòu),至于要在這些架構(gòu)上面做些什么,增加什么,就屬于派生類自己的問題。

class Human
{
public:
	virtual void print() = 0;
};
class Student : public Human
{
public:
	virtual void print()
	{
		cout << "i am a student" << endl;
	}
};
class Teacher : public Human
{
public:
	virtual void print()
	{
		cout << "i am a teacher" << endl;
	}
};

接口繼承與實(shí)現(xiàn)繼承

普通函數(shù)的繼承就是接口繼承,派生類可以使用基類的函數(shù);而虛函數(shù)的重寫則是實(shí)現(xiàn)繼承,派生類繼承的僅僅是基類的函數(shù)接口,目的是為了重寫基類虛函數(shù)的函數(shù)體,達(dá)成多態(tài)。因此如果不實(shí)現(xiàn)多態(tài),則不要將函數(shù)定義為虛函數(shù)。

到此這篇關(guān)于C++多態(tài)的全面講解的文章就介紹到這了,更多相關(guān)C++多態(tài)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Qt出現(xiàn)假死凍結(jié)現(xiàn)象的原因及解決方法

    Qt出現(xiàn)假死凍結(jié)現(xiàn)象的原因及解決方法

    應(yīng)用程序出現(xiàn)假死或凍結(jié)現(xiàn)象通常是由于一些常見問題所導(dǎo)致的,本文主要介紹了Qt出現(xiàn)假死凍結(jié)現(xiàn)象的原因及解決方法,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-10-10
  • C++卸載程序功能示例

    C++卸載程序功能示例

    用C++寫一個(gè)自己的卸載程序來完成程序的卸載工作,同時(shí)運(yùn)行后要?jiǎng)h除卸載程序本身,并刪除卸載程序所在的文件夾
    2013-11-11
  • c語言?數(shù)據(jù)存儲(chǔ)與原碼?反碼?補(bǔ)碼詳細(xì)解析

    c語言?數(shù)據(jù)存儲(chǔ)與原碼?反碼?補(bǔ)碼詳細(xì)解析

    不知道你是否和我一樣好奇,學(xué)習(xí)編程語言的同時(shí)想,各個(gè)數(shù)據(jù)類型是怎樣在我們的內(nèi)存中儲(chǔ)存的呢,如果你仔細(xì)深入了解的話,你會(huì)了解其中的樂趣,了解科學(xué)家們的偉大,了解c語言
    2022-02-02
  • C++加密解密php代碼的方法

    C++加密解密php代碼的方法

    這篇文章主要介紹了C++加密解密php代碼的方法,實(shí)例分析了基于C++實(shí)現(xiàn)加密解密的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-07-07
  • C++之編寫高效Makefile文件最佳方法

    C++之編寫高效Makefile文件最佳方法

    在軟件開發(fā)過程中,Makefile是一個(gè)非常重要的工具,它可以幫助我們自動(dòng)化構(gòu)建、編譯、測(cè)試和部署,然而,編寫高效的Makefile文件并不是一件容易的事情。在本文中,我們將討論如何編寫高效的Makefile文件,以提高開發(fā)效率和產(chǎn)品質(zhì)量,需要的朋友可以參考下
    2023-05-05
  • 關(guān)于嘗試開發(fā)PHP的MYSQL擴(kuò)展的使用

    關(guān)于嘗試開發(fā)PHP的MYSQL擴(kuò)展的使用

    本篇文章小編將為大家介紹,關(guān)于嘗試開發(fā)PHP的MYSQL擴(kuò)展的使用,需要的朋友可以參考一下
    2013-04-04
  • C++算法學(xué)習(xí)之回溯法的應(yīng)用

    C++算法學(xué)習(xí)之回溯法的應(yīng)用

    這篇文章介紹了C++算法中回溯法的一些應(yīng)用,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-05-05
  • C語言舉例講解轉(zhuǎn)義字符的使用

    C語言舉例講解轉(zhuǎn)義字符的使用

    轉(zhuǎn)義字符是很多程序語言、數(shù)據(jù)格式和通信協(xié)議的形式文法的一部分。對(duì)于一個(gè)給定的字母表,一個(gè)轉(zhuǎn)義字符的目的是開始一個(gè)字符序列,使得轉(zhuǎn)義字符開頭的該字符序列具有不同于該字符序列單獨(dú)出現(xiàn)(沒有轉(zhuǎn)義字符開頭)時(shí)的語義。因此轉(zhuǎn)義字符開頭的字符序列被叫做轉(zhuǎn)義序列
    2022-05-05
  • C++?Boost?Xpressive示例分析使用

    C++?Boost?Xpressive示例分析使用

    Boost是為C++語言標(biāo)準(zhǔn)庫提供擴(kuò)展的一些C++程序庫的總稱。Boost庫是一個(gè)可移植、提供源代碼的C++庫,作為標(biāo)準(zhǔn)庫的后備,是C++標(biāo)準(zhǔn)化進(jìn)程的開發(fā)引擎之一,是為C++語言標(biāo)準(zhǔn)庫提供擴(kuò)展的一些C++程序庫的總稱
    2022-11-11
  • C/C++ 編譯器優(yōu)化介紹

    C/C++ 編譯器優(yōu)化介紹

    這篇文章主要涉及了C/C++ 編譯器優(yōu)化的簡(jiǎn)單介紹,具有一定參考價(jià)值。如有不對(duì)之處,歡迎指出。
    2017-09-09

最新評(píng)論