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

C++多態(tài)定義及實(shí)現(xiàn)深度剖析

 更新時(shí)間:2025年09月17日 08:40:44   作者:禁止默  
多態(tài)也是面向?qū)ο蟪绦蛟O(shè)計(jì)的重要特性,它都是配合繼承來(lái)使用,多態(tài)可以增加程序設(shè)計(jì)的靈活性,這篇文章主要介紹了C++多態(tài)定義及實(shí)現(xiàn)的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下

前言

在前面我們對(duì)C++的封裝,繼承等特性都有了了解和學(xué)習(xí),接下來(lái)我們將對(duì)C++的第三大特性-多態(tài)進(jìn)行認(rèn)識(shí)和掌握。內(nèi)容分為來(lái)兩大部分,第一個(gè)是對(duì)多態(tài)的認(rèn)識(shí)和運(yùn)用,第二大部分是對(duì)多態(tài)原理的了解和擴(kuò)展。

1.多態(tài)的概念

多態(tài)(Polymorphism)是面向?qū)ο缶幊蹋∣OP)中的一個(gè)核心概念,它指的是同一個(gè)行為具有多個(gè)不同表現(xiàn)形式或形態(tài)的能力。在編程中,多態(tài)通常通過(guò)繼承(inheritance)和接(interfaces來(lái)實(shí)現(xiàn)。

以下是多態(tài)的幾個(gè)主要方面:

  • 編譯時(shí)多態(tài)(靜態(tài)多態(tài)):這是在編譯時(shí)確定的多態(tài)性,通常通過(guò)函數(shù)重載(function overloading)和模板(templates)來(lái)實(shí)現(xiàn)。編譯器根據(jù)函數(shù)的參數(shù)類型或數(shù)量來(lái)決定調(diào)用哪個(gè)函數(shù)。

  • 運(yùn)行時(shí)多態(tài)(動(dòng)態(tài)多態(tài)):這是在程序運(yùn)行時(shí)確定的多態(tài)性,主要通過(guò)虛函數(shù)(virtual functions)和繼承來(lái)實(shí)現(xiàn)。在運(yùn)行時(shí),根據(jù)對(duì)象的實(shí)際類型來(lái)調(diào)用相應(yīng)的成員函數(shù)。

之所以叫編譯時(shí)多態(tài),是 因?yàn)樗麄儗?shí)參傳給形參的參數(shù)匹配是在編譯時(shí)完成的,我們把編譯時(shí)一般歸為靜態(tài),運(yùn)行時(shí)歸為動(dòng)態(tài)。

運(yùn)行時(shí)多態(tài),具體點(diǎn)就是去完成某個(gè)行為(函數(shù)),可以傳不同的對(duì)象就會(huì)完成不同的行為,就達(dá)到多種 形態(tài)。比如買票這個(gè)行為,當(dāng)普通人買票時(shí),是全價(jià)買票;學(xué)生買票時(shí),是優(yōu)惠買票(5折或75折);軍人買票時(shí)是優(yōu)先買票。再比如,同樣是動(dòng)物叫的一個(gè)行為(函數(shù)),傳貓對(duì)象過(guò)去,就是”(>^ω^<)喵“,傳狗對(duì)象過(guò)去,就是"汪汪"。

多態(tài)的關(guān)鍵特性包括:

  • 繼承:子類繼承父類的屬性和行為,可以對(duì)這些行為進(jìn)行重寫(override)。
  • 虛函數(shù):在基類中聲明為虛的成員函數(shù),可以在派生類中被重寫,使得通過(guò)基類指針或引用調(diào)用函數(shù)時(shí),能夠根據(jù)對(duì)象的實(shí)際類型來(lái)調(diào)用相應(yīng)的函數(shù)版本。
  • 虛函數(shù)表:用于實(shí)現(xiàn)運(yùn)行時(shí)多態(tài)的數(shù)據(jù)結(jié)構(gòu),它存儲(chǔ)了虛函數(shù)的地址,使得程序能夠在運(yùn)行時(shí)確定調(diào)用哪個(gè)函數(shù)。
  • 向上轉(zhuǎn)型:將派生類對(duì)象的引用或指針轉(zhuǎn)換為基類類型的引用或指針,這是多態(tài)實(shí)現(xiàn)的基礎(chǔ)。

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

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

多態(tài)是一個(gè)繼承關(guān)系的下的類對(duì)象,去調(diào)用同一函數(shù),產(chǎn)生了不同的行為。比如Student繼承了

Person。Person對(duì)象買票全價(jià),Student對(duì)象優(yōu)惠買票。

2.1.1重要條件

被調(diào)用的函數(shù)必須是虛函數(shù)

指針或者引用調(diào)用虛函數(shù)

說(shuō)明:要實(shí)現(xiàn)多態(tài)效果,第一必須是基類的指針或引用,因?yàn)橹挥谢惖闹羔樆蛞貌拍芗戎赶蚺缮?nbsp;類對(duì)象;第二派生類必須對(duì)基類的虛函數(shù)重寫/覆蓋,重寫或者覆蓋了,派生類才能有不同的函數(shù),多 態(tài)的不同形態(tài)效果才能達(dá)到。

2.1.2 虛函數(shù)

類成員函數(shù)前面加virtual修飾,那么這個(gè)成員函數(shù)被稱為虛函數(shù)。注意?成員函數(shù)不能加virtual修

飾。

class Person {
public:
	virtual void BuyTicket() {
		cout << "買票全額" << endl;
	}
};

2.1.3 虛函數(shù)的重寫/覆蓋

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

注意:在重寫基類虛函數(shù)時(shí),派生類的虛函數(shù)在不加virtual關(guān)鍵字時(shí),雖然也可以構(gòu)成重寫(因?yàn)槔^承 后基類的虛函數(shù)被繼承下來(lái)了在派生類依舊保持虛函數(shù)屬性),但是該種寫法不是很規(guī)范,不建議這樣 使用,不過(guò)在考試選擇題中,經(jīng)常會(huì)故意買這個(gè)坑,讓判斷是否構(gòu)成多態(tài)。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Person {
public:
	virtual void BuyTicket() {
		cout << "買票全額" << endl;
	}
};
class Student : public Person {
public:
	virtual void BuyTicket() {
		cout << "學(xué)生票半價(jià)" << endl;
	}
};
//引用調(diào)用
void func(Person& p) {
	p.BuyTicket();
}
//指針調(diào)用
void func1(Person* p) {
	p->BuyTicket();
	// 這?可以看到雖然都是Person指針Ptr在調(diào)?BuyTicket
    // 但是跟ptr沒(méi)關(guān)系,?是由ptr指向的對(duì)象決定的。
}
int main() {
	Person p1;
	Student s1;
	Person* p2 = new Person();
	Student* s2 = new Student();
	func(p1);
	func(s1);
	p1.BuyTicket();
	s1.BuyTicket();
	func1(&p1);
	func1(&s1);
	p2->BuyTicket();
	s2->BuyTicket();

	return 0;
}

void func(Student& p) {
    p.BuyTicket();
}
//指針調(diào)用
void func1(Student* p) {
    p->BuyTicket();
}

如果改成Student,就會(huì)出問(wèn)題,就不是多態(tài)了,也就不能傳Person對(duì)象了。 

#include <iostream>
using namespace std;

class Pet {
public:
	virtual void eat() const{
		cout << "Eat food" << endl;
	}
};
class Dog : public Pet{
public:
	virtual void eat() const {
		cout << "Dog eats meat!" << endl;
	}
};
class Cat :public Pet {
public:
	virtual void eat()const {
		cout << "Cat eats fish!" << endl;
	}
};
void func(const Pet& p) {
	p.eat();
}
int main() {
	Pet p;
	Dog g;
	Cat c;
	func(p);
	func(g);
	func(c);
	return 0;
}

 上述是寵物的一個(gè)多態(tài)實(shí)現(xiàn)。

這里我們測(cè)試一下,基類函數(shù)不加virtual會(huì)怎樣,

class Pet {
public:
     void eat() const{
        cout << "Eat food" << endl;
    }
};

我們會(huì)發(fā)現(xiàn)多態(tài)效果沒(méi)有實(shí)現(xiàn),所以一定要加上virtual. 

2.1.4 選擇題

下面程序輸出結(jié)果是什么?(B)

A: A->0 B: B->1 C: A->1 D: B->0 E: 編譯出錯(cuò) F: 以上都不正確

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* p = new B; 創(chuàng)建了一個(gè) B 類型的對(duì)象,并通過(guò)基類指針 p 指向它。
  • p->test(); 調(diào)用了 A 類的 test 方法(因?yàn)?nbsp;B 類沒(méi)有重寫 test 方法)。
  • 在 A 類的 test 方法中,func(val) 被調(diào)用,沒(méi)有指定 val 的值,因此它使用 A 類 func 方法的默認(rèn)參數(shù) 1
  • 由于 func 是虛函數(shù),并且 p 指向一個(gè) B 類型的對(duì)象,所以 B 類的 func 方法被調(diào)用,接收到的參數(shù)是 1。

2.1.5 虛函數(shù)其他知識(shí)

協(xié)變(了解)

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

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

class A {};
class B : public A {};
class Person {
public:
	virtual A* BuyTicket()
	{
		cout << "買票-全價(jià)" << endl;
		return nullptr;
	}
};
class Student : public Person {
public:
	virtual B* BuyTicket()
	{
		cout << "買票-打折" << endl;
		return nullptr;
	}
};
void Func(Person* ptr)
{
	ptr->BuyTicket();
}
int main()
{
	Person ps;
	Student st;
	Func(&ps);
	Func(&st);
	return 0;
}

 析構(gòu)函數(shù)的重寫

基類的析構(gòu)函數(shù)為虛函數(shù),此時(shí)派生類析構(gòu)函數(shù)只要定義,無(wú)論是否加virtual關(guān)鍵字,都與基類的析構(gòu)函數(shù)構(gòu)成重寫,雖然基類與派生類析構(gòu)函數(shù)名字不同看起來(lái)不符合重寫的規(guī)則,實(shí)際上編譯器對(duì)析構(gòu)函數(shù)的名稱做了特殊處理,編譯后析構(gòu)函數(shù)的名稱統(tǒng)一處理成destructor, 所以基類的析構(gòu)函數(shù)加了vialtual修飾,派生類的析構(gòu)函數(shù)就構(gòu)成重寫。

故在C++中,當(dāng)一個(gè)基類的析構(gòu)函數(shù)被聲明為虛函數(shù)時(shí),它確保了當(dāng)通過(guò)基類指針或引用刪除派生類對(duì)象時(shí),會(huì)調(diào)用正確的析構(gòu)函數(shù),即派生類的析構(gòu)函數(shù),然后再調(diào)用基類的析構(gòu)函數(shù)。這是因?yàn)樘撐鰳?gòu)函數(shù)允許動(dòng)態(tài)綁定,確保了派生類對(duì)象被正確地銷毀。

#include <iostream>
using namespace std;
class A {
public:
	virtual ~A() {
		cout << "delete A" << endl;
	}
}; 
class B :public A {
	public:
		~B() {
			cout << "~B()->delete:" << _p << endl;
			delete _p;
		}
protected:
	int* _p = new int[10];
};
int main() {
	A* a = new A;
	A* b = new B;
	delete a;
	delete b;
	return 0;
}

當(dāng)我們不把基類析構(gòu)函數(shù)設(shè)置成virtual時(shí), 會(huì)發(fā)現(xiàn)沒(méi)有調(diào)用B的析構(gòu),該釋放的資源沒(méi)有釋放掉。

public:
	~A() {
		cout << "delete A" << endl;
	}
}; 

故基類的析構(gòu)函數(shù)我們要設(shè)置成虛函數(shù)。

override 和 final關(guān)鍵字

從上面可以看出,C++對(duì)函數(shù)重寫的要求比較嚴(yán)格,但是有些情況下由于疏忽,比如函數(shù)名寫錯(cuò)參數(shù)寫錯(cuò)等導(dǎo)致無(wú)法構(gòu)成重載,而這種錯(cuò)誤在編譯期間是不會(huì)報(bào)出的,只有在程序運(yùn)行時(shí)沒(méi)有得到預(yù)期結(jié)果才來(lái)debug會(huì)得不償失。

如果不想讓派生類重寫這個(gè)虛函數(shù),那么可以用final去修飾。

在C++中,override 和 final 關(guān)鍵字是C++11標(biāo)準(zhǔn)引入的,用于增強(qiáng)類繼承和虛函數(shù)的聲明。

override 關(guān)鍵字用于明確指出一個(gè)成員函數(shù)旨在重寫(覆蓋)其基類中的一個(gè)虛函數(shù)。如果該函數(shù)沒(méi)有正確地重寫基類中的任何虛函數(shù),編譯器將報(bào)錯(cuò)。這有助于避免因拼寫錯(cuò)誤或參數(shù)列表不匹配而意外地沒(méi)有重寫虛函數(shù)的情況。

	class Car {
	public:
		virtual void Dirve()
		{}
	};
	class Benz :public Car {
	public:
		virtual void Drive() override { cout << "Benz-舒適" << endl; }
	};

比如上面這個(gè)例子,函數(shù)名寫錯(cuò)了,重寫失敗,編譯報(bào)錯(cuò)。

final 關(guān)鍵字用于防止類被進(jìn)一步派生,或者防止虛函數(shù)被重寫。當(dāng)應(yīng)用于類時(shí),它表示這個(gè)類不能被繼承。當(dāng)應(yīng)用于虛函數(shù)時(shí),它表示這個(gè)虛函數(shù)不能在派生類中被重寫。

class Car {
public:
	virtual void Dirve() final
	{}
};
class Benz :public Car {
public:
	virtual void Dirve(){ cout << "Benz-舒適" << endl; }
};

class Base final { // 不能從這個(gè)類派生其他類
public:
    virtual void doSomething() const final {} // 這個(gè)虛函數(shù)不能被重寫
};
// 下面的類聲明會(huì)導(dǎo)致編譯錯(cuò)誤,因?yàn)?Base 是 final 的
// class Derived : public Base {};
// 下面的函數(shù)聲明也會(huì)導(dǎo)致編譯錯(cuò)誤,因?yàn)?doSomething 是 final 的
// class Derived : public Base {
// public:
//     void doSomething() const override {} // 錯(cuò)誤:不能重寫 final 函數(shù)
// };

使用 final 關(guān)鍵字可以確保類或虛函數(shù)的行為不會(huì)被意外的繼承或重寫改變,這對(duì)于設(shè)計(jì)那些不打算被擴(kuò)展的類或函數(shù)非常有用。 

3. 重載,重寫,隱藏的對(duì)比

重載(Overloading)

  • 定義:在同一作用域內(nèi),可以定義多個(gè)同名函數(shù),只要它們的參數(shù)列表(參數(shù)的數(shù)量、類型或順序)不同。
  • 特點(diǎn)
    • 發(fā)生在同一類中。
    • 參數(shù)列表必須不同。
    • 返回類型可以不同,但不是區(qū)分重載的主要因素。

重寫(Overriding)

  • 定義:在派生類中提供一個(gè)與基類中虛函數(shù)同名、參數(shù)列表和返回類型相同的函數(shù),以實(shí)現(xiàn)多態(tài)。
  • 特點(diǎn)
    • 發(fā)生在基類和派生類之間。
    • 參數(shù)列表和返回類型必須相同。
    • 基類函數(shù)必須是虛函數(shù)。
    • 使用 override 關(guān)鍵字可以明確指出重寫意圖。

 隱藏(Hiding)

  • 定義:在派生類中定義一個(gè)與基類中成員(非虛函數(shù)或非靜態(tài)成員變量)同名的成員,導(dǎo)致基類中的同名成員在派生類中不可見(jiàn)。
  • 特點(diǎn)
    • 發(fā)生在基類和派生類之間。
    • 可以是函數(shù)或變量。
    • 如果是函數(shù),參數(shù)列表不必相同。
    • 如果派生類中的成員與基類中的成員具有相同的名稱,但不同的參數(shù)列表,則基類成員被隱藏,而不是重載或重寫。

4.純虛函數(shù)和抽象類

在虛函數(shù)的后面寫上 =0 ,則這個(gè)函數(shù)為純虛函數(shù),純虛函數(shù)不需要定義實(shí)現(xiàn)(實(shí)現(xiàn)沒(méi)啥意義因?yàn)橐慌缮愔貙?,但是語(yǔ)法上可以實(shí)現(xiàn)),只要聲明即可。

包含純虛函數(shù)的類叫做抽象類,抽象類不能實(shí)例化出對(duì)象,如果派生類繼承后不重寫純虛函數(shù),那么派生類也是抽象類。純虛函數(shù)某種程度上強(qiáng)制了派生類重寫虛函數(shù),因?yàn)椴恢貙憣?shí)例化不出對(duì)象。

#include <iostream>
using namespace std;
class Car {
public:
	virtual void Drive() = 0;

};
class Benchi :public Car {
public:
	virtual void Drive() {
		cout << "Benchi-舒適" << endl;
	}
};

class Baoma :public Car {
public:
	virtual void Drive() {
		cout << "Baoma-上手" << endl;
	}
};
int main() {
	Car car;
	Car* b = new Benchi();
	b->Drive();
	Car* m = new Baoma();
	m->Drive();
	return 0;
}

 這里Car是抽象類,所以無(wú)法實(shí)例化對(duì)象。

結(jié)束語(yǔ)

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

相關(guān)文章

  • C語(yǔ)言編程使用MATLAB繪制橢圓及圓角矩形

    C語(yǔ)言編程使用MATLAB繪制橢圓及圓角矩形

    這篇文章主要為大家介紹了C語(yǔ)言編程中使用MATLAB繪制橢圓及圓角矩形的實(shí)現(xiàn)源碼,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2022-02-02
  • c++基礎(chǔ)使用STL的注意點(diǎn)詳解

    c++基礎(chǔ)使用STL的注意點(diǎn)詳解

    這篇文章主要為大家介紹了c++基礎(chǔ)使用STL的注意點(diǎn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2021-12-12
  • C語(yǔ)言實(shí)現(xiàn)文件讀寫功能流程

    C語(yǔ)言實(shí)現(xiàn)文件讀寫功能流程

    這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)文件讀寫,文件是一段數(shù)據(jù)的集合,這些數(shù)據(jù)可以是有規(guī)則的,也可以是無(wú)序的集合。在stdio.h有一個(gè)非常重要的東西,文件指針,每個(gè)文件都會(huì)在內(nèi)存中開辟一塊空間,用于存放文件的相關(guān)信息
    2022-12-12
  • 基于linux下獲取時(shí)間函數(shù)的詳解

    基于linux下獲取時(shí)間函數(shù)的詳解

    本篇文章是對(duì)linux下獲取時(shí)間的函數(shù)進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • C++中vector可以作為map的鍵值實(shí)例代碼

    C++中vector可以作為map的鍵值實(shí)例代碼

    這篇文章主要介紹了C++中vector可以作為map的鍵值實(shí)例代碼,需要的朋友可以參考下
    2017-07-07
  • QT的幾種QMap插入順序說(shuō)明

    QT的幾種QMap插入順序說(shuō)明

    本文主要介紹了QT的幾種QMap插入順序說(shuō)明,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2025-09-09
  • C++迭代器失效解決辦法詳解

    C++迭代器失效解決辦法詳解

    這篇文章主要介紹了迭代器失效的概念,以及在vector、list和map等容器中插入和刪除操作導(dǎo)致迭代器失效的情況,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-12-12
  • 利用QT實(shí)現(xiàn)圖片瀏覽器的示例詳解

    利用QT實(shí)現(xiàn)圖片瀏覽器的示例詳解

    這篇文章主要和大家分享一個(gè)小案例:利用QT制作一個(gè)小的圖片瀏覽器,要求可以顯示jpg、jpeg、png、bmp,還可以從電腦上拖動(dòng)圖到窗口并顯示出來(lái),感興趣的可以了解一下
    2023-02-02
  • 使用Matlab制作簡(jiǎn)易版八分音符醬游戲

    使用Matlab制作簡(jiǎn)易版八分音符醬游戲

    八分音符醬作為一款聲音控制類游戲,當(dāng)時(shí)還是很受大家的喜愛(ài)的。本文將用Matlab制作一款簡(jiǎn)易版的八分音符醬游戲,感興趣的可以學(xué)習(xí)一下
    2022-02-02
  • 對(duì)稱矩陣的壓縮儲(chǔ)存講解

    對(duì)稱矩陣的壓縮儲(chǔ)存講解

    今天小編就為大家分享一篇關(guān)于對(duì)稱矩陣的壓縮儲(chǔ)存講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-02-02

最新評(píng)論