亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

前言

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

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

多態(tài)(Polymorphism)是面向對象編程(OOP)中的一個核心概念,它指的是同一個行為具有多個不同表現(xiàn)形式或形態(tài)的能力。在編程中,多態(tài)通常通過繼承(inheritance)和接(interfaces來實現(xiàn)。

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

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

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

之所以叫編譯時多態(tài),是 因為他們實參傳給形參的參數(shù)匹配是在編譯時完成的,我們把編譯時一般歸為靜態(tài),運行時歸為動態(tài)。

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

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

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

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

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

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

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

2.1.1重要條件

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

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

說明:要實現(xiàn)多態(tài)效果,第一必須是基類的指針或引用,因為只有基類的指針或引用才能既指向派生 類對象;第二派生類必須對基類的虛函數(shù)重寫/覆蓋,重寫或者覆蓋了,派生類才能有不同的函數(shù),多 態(tài)的不同形態(tài)效果才能達到。

2.1.2 虛函數(shù)

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

飾。

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

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

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

注意:在重寫基類虛函數(shù)時,派生類的虛函數(shù)在不加virtual關鍵字時,雖然也可以構成重寫(因為繼承 后基類的虛函數(shù)被繼承下來了在派生類依舊保持虛函數(shù)屬性),但是該種寫法不是很規(guī)范,不建議這樣 使用,不過在考試選擇題中,經(jīng)常會故意買這個坑,讓判斷是否構成多態(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 << "學生票半價" << endl;
	}
};
//引用調用
void func(Person& p) {
	p.BuyTicket();
}
//指針調用
void func1(Person* p) {
	p->BuyTicket();
	// 這?可以看到雖然都是Person指針Ptr在調?BuyTicket
    // 但是跟ptr沒關系,?是由ptr指向的對象決定的。
}
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();
}
//指針調用
void func1(Student* p) {
    p->BuyTicket();
}

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

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

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

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

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

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

2.1.4 選擇題

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

A: A->0 B: B->1 C: A->1 D: B->0 E: 編譯出錯 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)建了一個 B 類型的對象,并通過基類指針 p 指向它。
  • p->test(); 調用了 A 類的 test 方法(因為 B 類沒有重寫 test 方法)。
  • 在 A 類的 test 方法中,func(val) 被調用,沒有指定 val 的值,因此它使用 A 類 func 方法的默認參數(shù) 1。
  • 由于 func 是虛函數(shù),并且 p 指向一個 B 類型的對象,所以 B 類的 func 方法被調用,接收到的參數(shù)是 1

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

協(xié)變(了解)

派生類重寫基類虛函數(shù)時,與基類虛函數(shù)返回值類型不同。即基類虛函數(shù)返回基類對象的指針或者引用,派生類虛函數(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 << "買票-全價" << 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;
}

 析構函數(shù)的重寫

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

故在C++中,當一個基類的析構函數(shù)被聲明為虛函數(shù)時,它確保了當通過基類指針或引用刪除派生類對象時,會調用正確的析構函數(shù),即派生類的析構函數(shù),然后再調用基類的析構函數(shù)。這是因為虛析構函數(shù)允許動態(tài)綁定,確保了派生類對象被正確地銷毀。

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

當我們不把基類析構函數(shù)設置成virtual時, 會發(fā)現(xiàn)沒有調用B的析構,該釋放的資源沒有釋放掉。

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

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

override 和 final關鍵字

從上面可以看出,C++對函數(shù)重寫的要求比較嚴格,但是有些情況下由于疏忽,比如函數(shù)名寫錯參數(shù)寫錯等導致無法構成重載,而這種錯誤在編譯期間是不會報出的,只有在程序運行時沒有得到預期結果才來debug會得不償失。

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

在C++中,override 和 final 關鍵字是C++11標準引入的,用于增強類繼承和虛函數(shù)的聲明。

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

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

比如上面這個例子,函數(shù)名寫錯了,重寫失敗,編譯報錯。

final 關鍵字用于防止類被進一步派生,或者防止虛函數(shù)被重寫。當應用于類時,它表示這個類不能被繼承。當應用于虛函數(shù)時,它表示這個虛函數(shù)不能在派生類中被重寫。

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

class Base final { // 不能從這個類派生其他類
public:
    virtual void doSomething() const final {} // 這個虛函數(shù)不能被重寫
};
// 下面的類聲明會導致編譯錯誤,因為 Base 是 final 的
// class Derived : public Base {};
// 下面的函數(shù)聲明也會導致編譯錯誤,因為 doSomething 是 final 的
// class Derived : public Base {
// public:
//     void doSomething() const override {} // 錯誤:不能重寫 final 函數(shù)
// };

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

3. 重載,重寫,隱藏的對比

重載(Overloading)

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

重寫(Overriding)

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

 隱藏(Hiding)

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

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

在虛函數(shù)的后面寫上 =0 ,則這個函數(shù)為純虛函數(shù),純虛函數(shù)不需要定義實現(xiàn)(實現(xiàn)沒啥意義因為要被派生類重寫,但是語法上可以實現(xiàn)),只要聲明即可。

包含純虛函數(shù)的類叫做抽象類,抽象類不能實例化出對象,如果派生類繼承后不重寫純虛函數(shù),那么派生類也是抽象類。純虛函數(shù)某種程度上強制了派生類重寫虛函數(shù),因為不重寫實例化不出對象。

#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是抽象類,所以無法實例化對象。

結束語

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

相關文章

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

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

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

    c++基礎使用STL的注意點詳解

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

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

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

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

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

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

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

    QT的幾種QMap插入順序說明

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

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

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

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

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

    使用Matlab制作簡易版八分音符醬游戲

    八分音符醬作為一款聲音控制類游戲,當時還是很受大家的喜愛的。本文將用Matlab制作一款簡易版的八分音符醬游戲,感興趣的可以學習一下
    2022-02-02
  • 對稱矩陣的壓縮儲存講解

    對稱矩陣的壓縮儲存講解

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

最新評論