【C++grammar】多態、聯編、虛函數

目錄

  • 1、多態概念
    • 1.多態性有兩種表現的方式
  • 2、聯編(實現多態)
    • 1.靜態聯編
    • 2.動態聯編
  • 3、實現運行時多態
    • 1.為何要使用運行時多態?
    • 2.如何實現運行時多態
    • 3.多態的例子
      • 1.調用哪個同名虛函數?
      • 2. 用途:可以用父類指針訪問子類對象成員
    • 4. 虛函數的傳遞性
      • 虛函數的缺點:
  • 4、代碼示例
    • 1、繼承與重載函數的例子
      • 任務1、2
      • 任務3
    • 2、使用運行時多態
    • 3、override覆寫
  • 6、運行時多態的總結
    • 1.Summary: static binding v.s. dynamic binding
    • 2. Summary: 靜態聯編的簡單示例
    • 3. Summary: 通過基類指針訪問同名函數的示例
    • 4. Summary:動態聯編的簡單示例-指針形式
    • 5. Summary:動態聯編的簡單示例-引用形式
  • 7、C++11:使用override和final
    • 1. override顯式聲明覆寫
    • 2. final 顯式聲明禁止覆寫

1、多態概念

廣義的多態:不同類型的實體/對象對于同一消息(可以理解為同一個名字的函數)有不同的響應,就是OOP中的多態性。

1.多態性有兩種表現的方式

1. 重載多態:調用參數不同的同名函數,表現出不同的行為

class C {
public: int f(int x);int f( ); 
};

2.子類型多態:不同的對象調用同名重定義函數,表現出不同的行為

class A           { virtual int f() {return 1;} };
class B: public A { virtual int f() {return 8;} };
A  a; B b;
A* p = &b;	//p雖然是a類型,但是它指向B類型的對象
a.f()   // call A::f()
b.f()   // call B::f()
p->f(); // call B::f()
//a和p是同類型,但是調用同名函數的響應是不一樣的。

2、聯編(實現多態)

確定具有多態性的語句調用哪個函數的過程稱為聯編。
在這里插入圖片描述

1.靜態聯編

靜態聯編在程序編譯時(Compile-time)確定調用哪個函數。
如:函數重載

2.動態聯編

在程序運行時(Run-time),才能夠確定調用哪個函數
用動態聯編實現的多態,也稱為運行時多態(Run-time Polymorphism)。

3、實現運行時多態

1.為何要使用運行時多態?

我們有三個類ABC,繼承鏈如下:
A<-B<-C;
我們想使用print()調用toString()輸出信息,就需要寫三個重載函數:

void print(A obj);
void print(B obj);
void print(C obj);

這樣比較麻煩。
我們可以使用運行時多態解決這個問題:

2.如何實現運行時多態

實現運行時多態有兩個要素:

(1) virtual function (虛函數)
(2) Override (覆寫) : redefining a virtual function in a derived class. (在派生類中重定義一個虛函數)

在正常的函數名字前加上virtual關鍵字,這個函數就會變為虛函數:

struct A{virtual std::string toString(){return "A";}
};

覆寫是在派生類中定義一個與基類虛函數同名,參數一樣,返回值一樣的函數。
注意這里我們傳遞的是ABC類型的指針:
在這里插入圖片描述

3.多態的例子

1.調用哪個同名虛函數?

(1) 不由指針類型決定;

(2) 而由指針所指的【實際對象】的類型決定

(3) 運行時,檢查指針所指對象類型

2. 用途:可以用父類指針訪問子類對象成員

注意print函數的參數是基類類型的指針。
然后將ABC類型的對象的地址作為函數參數傳入。
后面兩個print函數指針會做隱式類型轉換。將派生類的地址轉化為基類類型的指針,這個轉換是安全的。
返回 的結果是不一樣的,是和對象的實際情況相關的。
在這里插入圖片描述

4. 虛函數的傳遞性

基類定義了虛同名函數,那么派生類中的同名函數自動變為虛函數。
注意:只需要在繼承連上最頂端的基類上加上關鍵字即可:
在這里插入圖片描述

虛函數的缺點:

1、類中保存著一個Virtual function table (虛函數表)。
2、運行時聯編/動態聯編,會有額外的邏輯。
所以調用虛函數比非虛函數開銷大

4、代碼示例

1、繼承與重載函數的例子

任務1、2

1、創建A/B/C三個類,B繼承A,C繼承B,ABC均有toString函數
2、創建print函數,接受A類型的參數,調用A對象的toString()

不管傳進來什么類型,都是打印基類類型的數據

//本部分要展示的內容如下:
//1、創建A/B/C三個類,B繼承A,C繼承B,ABC均有toString函數
//2、創建print函數,接受A類型的參數,調用A對象的toString()
//3、重載print函數,接受B/C類型參數,調用toStirng()#include<iostream>
#include<string>
using std::cout;
using std::endl;
class A {
public:std::string toString() { return "A"; }
};
class B : public A {
public:std::string toString() { return "B"; }
};
class C : public B {
public:std::string toString() { return "C"; }
};void print(A a) {cout << a.toString() << endl;
}
int main()
{A a;B b;C c;print(a);print(b);print(c);
}

在這里插入圖片描述

任務3

重載print函數,接受B/C類型參數,調用toStirng()

//本部分要展示的內容如下:
//1、創建A/B/C三個類,B繼承A,C繼承B,ABC均有toString函數
//2、創建print函數,接受A類型的參數,調用A對象的toString()
//3、重載print函數,接受B/C類型參數,調用toStirng()#include<iostream>
#include<string>
using std::cout;
using std::endl;
class A {
public:std::string toString() { return "A"; }
};
class B : public A {
public:std::string toString() { return "B"; }
};
class C : public B {
public:std::string toString() { return "C"; }
};void print(A a) {cout << a.toString() << endl;
}
void print(B b) {cout << b.toString() << endl;
}
void print(C c) {cout << c.toString() << endl;
}
int main()
{A a;B b;C c;print(a);print(b);print(c);
}

在這里插入圖片描述
通過三個重載函數,將打印的數據與類型匹配。

2、使用運行時多態

本部分要展示的內容如下:
1、將基類A的toStirng函數改為虛函數
2、將print函數參數改為基類指針類型
main()中調用print(),實參為指向對象的基類指針
3、添加一個print函數,參數是基類引用類型
在main()中調用print(),參數為對象的基類引用

//展示運行時多態的實現
#include<iostream>
#include<string>
using std::cout;
using std::endl;
class A {
public:virtual std::string toString() { return "A"; }
};
class B : public A {
public:std::string toString() { return "B"; }
};
class C : public B {
public:std::string toString() { return "C"; }
};void print(A* a) {cout << a->toString() << endl;
}
void print(A& a) {cout << a.toString() << endl;
}
int main()
{A a;B b;C c;A* p1 = &a;A* p2 = &b;A* p3 = &c;print(p1);print(p2);print(p3);print(a);print(b);print(c);
}

在這里插入圖片描述
可以發現參數是基類引用類型與參數是基類引用類型實現的效果是一樣的。這些就是多態的具體展示。
注意如果在print函數中沒有找到該對象的同名函數,那么就會順著繼承鏈,直到在基類中找到這個同名函數。
也就是說,在繼承鏈中如果某個類的虛函數名字寫錯了就會出現許多BUG。

3、override覆寫

為了解決上面的問題,c++提供了覆寫操作。這在第7大點將詳細講述:
在這里插入圖片描述
表明了對基類函數的覆寫,如果函數名字或者參數或者返回值不一樣,編譯器就會報錯,提醒程序員。

6、運行時多態的總結

1.Summary: static binding v.s. dynamic binding

基類與派生類中有同名函數
(1) 通過派生類對象訪問同名函數,是靜態聯編
(2) 通過基類對象的指針訪問同名函數,是靜態聯編
(3) 通過基類對象的指針或引用訪問同名虛函數,是動態聯編

2. Summary: 靜態聯編的簡單示例

class P           { public: f(){} }; //父類
class C: public P { public: f(){} }; //子類
main () {P p;   C c;p.f();         //調用P::f()c.f();         //調用C::f()
}

說明: 對象是什么類型,就調什么類型

3. Summary: 通過基類指針訪問同名函數的示例

class P           { public: f(){} }; //父類
class C: public P { public: f(){} }; //子類
main () {P* ptr;   P p;   C c;ptr = &p;ptr->f();      // 調用P::f()ptr=&c;ptr->f();      // 調用P::f()
}

說明: 指針是什么類型,就調什么類型

4. Summary:動態聯編的簡單示例-指針形式

class P           { public: virtual f(){} }; //父類
class C: public P { public: f(){} }; //子類,f自動virtual
main () {P* ptr;   P p;   C c;ptr = &p;ptr->f();    //調用P::f()ptr=&c;ptr->f();    //調用C::f()
}

說明: 函數虛,不看指針看真對象

5. Summary:動態聯編的簡單示例-引用形式

class P           { public: virtual f(){} }; //父類
class C:public P { public: f(){} }; //子類,f自動virtual
main () {P p;   C c;P& pr1 = p;pr1.f();      //調用P::f()P& pr2 = c;pr2.f();      //調用C::f()
}

說明: 函數虛,不看引用看真對象

7、C++11:使用override和final

1. override顯式聲明覆寫

C++11引入override標識符,指定一個虛函數覆寫另一個虛函數。

class A {
public:virtual void foo() {}void bar() {}
};
class B : public A {
public://此處foo為常函數,與基類的foo函數不是同名覆寫函數void foo() const override { // 錯誤: B::foo 不覆寫 A::foo}                           // (簽名不匹配)void foo() override;   // OK : B::foo 覆寫 A::foovoid bar() override {} // 錯誤: A::bar 非虛
};
void B::foo() override {// 錯誤: override只能放到類內使用
}

override的價值在于:避免程序員在覆寫時錯命名或無虛函數導致隱藏bug
summary:
1、override標識符應該寫到派生類同名虛函數的后面
2、非虛函數不能覆寫
3、覆寫只能在類的內部使用

2. final 顯式聲明禁止覆寫

C++11引入final特殊標識符,指定派生類不能覆寫虛函數。

struct Base {virtual void foo();
};struct A : Base 
{ void foo() final; // A::foo 被覆寫且是最終覆寫void bar() final; // 錯誤:非虛函數不能被覆寫或是 final
};
struct B final : A // struct B 為 final,不能被繼承
{void foo() override; // 錯誤: foo 不能被覆寫,因為它在 A 中是 final
};

final標識符的作用:
在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/377714.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/377714.shtml
英文地址,請注明出處:http://en.pswp.cn/news/377714.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

一 MVC - HtmlHelper

HtmlHelper類位于System.Web.Mvc.Html之中主要有七個靜態類組成&#xff1a; FormExtensions - BeginForm, BeginRouteForm, EndForm InputExtensions - CheckBox, CheckBoxFor, Hidden, HiddenFor, Password, PasswordFor, RadioButton, RadioButtonFor, TextBox, TextBoxFor …

HDOJ 400題紀念。

剛剛交了1506&#xff0c;無意間瞟到左邊的隨筆數&#xff0c;發現已經401題了&#xff0c;這么說前幾天就400題了啊囧。 昨天還想交到400題就先放放&#xff0c;背單詞的&#xff0c;沒想到那么快。等把USACO那個八皇后寫完吧。人生總是有許多不想做又不得不做的事情。。。 還…

二、用戶登錄和注冊

一、頁面設計 一共四個頁面 主頁面Form1&#xff0c;登錄頁面login&#xff0c;注冊頁面resister&#xff0c;主菜單頁面main_page 系統運行進入Form1&#xff0c;單擊登錄按鈕跳轉到login&#xff0c;數據庫中得存在數據信息且輸入正確才可登錄成功&#xff0c;跳轉到main_pa…

readdir函數_PHP readdir()函數與示例

readdir函數PHP readdir()函數 (PHP readdir() function) The full form of readdir is "Read Directory", the function readdir() is used to read the directory i.e. read the name of the next entry in the directory. readdir的完整形式為“ Read Directory”…

【C++grammar】訪問控制與抽象類與純虛函數

目錄一、訪問控制 (可見性控制)1.private、public、protected關鍵字2.關鍵字示例1、關鍵字對類數據成員訪問的限制3. 公有繼承4. 私有繼承5. 保護繼承6. 私有繼承和保護繼承的區別二、抽象類與純虛函數1.什么是抽象類2.抽象函數/純虛函數3.抽象類示例一、訪問控制 (可見性控制)…

mongodb 如何刪除 字段值為 json對象中的某個字段值

例如&#xff1a; { attributes: { birthday:1988-01-01, name: aq } } birthday是attributes字段的value的一個字段&#xff0c; 我要刪除birthday 用這句話&#xff1a; db.User.update({email:adminlinkris.com},{$unset:{attributes.birthday:}})轉載于:https://www.cnblog…

使用 Spring 的 Web 服務模擬器框架解決方案

http://www.ibm.com/developerworks/cn/web/wa-aj-simulator/index.html轉載于:https://www.cnblogs.com/diyunpeng/archive/2012/02/28/2371390.html

三、上傳織物圖片至SQL Server并提供name進行展示織物照片

一、數據庫的建立 還是在fiber_yy數據庫下創建images表 images表設計如下 二、頁面完善設計 main_page頁面進行功能完善 入庫管理系統 warehousing頁面 庫存查詢系統 query頁面 登錄注冊頁面前面幾個博文已經實現過了&#xff0c;這里就再贅述了&#xff0c;仍是沿用前…

gettype_PHP gettype()函數與示例

gettypePHP gettype()函數 (PHP gettype() function) In PHP, we have a library function gettype() to identify the type of data. The function is primarily used to sanity check the type of data being input in a variable. The function can identify the data into …

ARM MMU工作原理剖析[轉]

一、MMU的產生 許多年以前&#xff0c;當人們還在使用DOS或是更古老的操作系統的時候&#xff0c;計算機的內存還非常小&#xff0c;一般都是以K為單位進行計算&#xff0c;相應的&#xff0c;當時的程序規模也不大&#xff0c;所以內存容量雖然小&#xff0c;但還是可以容納當…

棧與隊列在SGI STL的底層實現

棧 棧提供push和pop等接口&#xff0c;不提供走訪功能&#xff0c;也不提供迭代器。 STL中棧不被歸類為容器&#xff0c;而被歸類為container adapter(容器適配器)&#xff0c;這是因為棧是以底層容器完成其所有的工作&#xff0c;對外提供統一的接口&#xff0c;底層容器是可…

【原創】SharePoint Document library List Check out 文檔時碰到的問題解決

環境&#xff1a;TFS(Team Foundation Server)集成的WSS 3.0&#xff08;SharePoint Service 3.0&#xff09; 問題&#xff1a;如題&#xff0c;祥見下圖 解決&#xff1a;一般碰到沒有經驗的問題&#xff0c;大家當然是外事不決問谷歌了&#xff0c;于是谷歌搜到了這篇博客 h…

getdate函數_PHP getdate()函數與示例

getdate函數PHP getdate()函數 (PHP getdate() function) getdate() function is used to get the local date/time (or it is also used to get the date/time based on the given timestamp. getdate()函數用于獲取本地日期/時間(或也用于根據給定的時間戳獲取日期/時間。 S…

四、入庫管理功能的完善

一、數據庫的創建 在fiber_yy數據庫下創建yy_textile表 先隨便添加幾條數據 二、頁面的完善 登錄注冊頁面我就不演示了&#xff0c;前幾篇博文也都有介紹 warehousing入庫頁面 main_page頁面進行功能完善 三、代碼實現 warehousing頁面 using System; using System.…

leetcode 232. 用棧實現隊列 思考分析

題目 請你僅使用兩個棧實現先入先出隊列。隊列應當支持一般隊列的支持的所有操作&#xff08;push、pop、peek、empty&#xff09;&#xff1a; 實現 MyQueue 類&#xff1a; void push(int x) 將元素 x 推到隊列的末尾 int pop() 從隊列的開頭移除并返回元素 int peek() 返…

YCSB初步介紹

隨著大數據時代的到來和云計算的不斷發展&#xff0c;作為云計算最基礎的設施存儲產品也越來越多&#xff0c;開源分布式存儲系統有BigTable-like系統HBase&#xff0c;dynamo-like系統Cassandra&#xff0c;voldemort&#xff0c;Riak&#xff0c;淘寶開源的OceanBase等。當然…

kotlin實現繼承_Kotlin程序| 繼承的例子

kotlin實現繼承遺產 (Inheritance) Inheritance is a mechanism wherein a new class is derived from an existing class. 繼承是一種機制&#xff0c;其中新類是從現有類派生的。 All Kotlin Classes have a common Superclass Any, it is the Default Superclass with no S…

【C++grammar】動態類型轉換、typeid與RTTI

目錄動態類型轉換1、為何需要動態類型轉換2、dynamic_cast<>();運算符3、向上轉換和向下轉換( Upcasting and Downcasting)4、 基類對象和派生類對象的互操作5、Upcasting/Downcasting與繼承鏈上不同類的對象之間的賦值有什么關系和區別&#xff1f;typeid 運行時查詢類型…

nginx資源定向 css js路徑問題

今天玩玩項目&#xff0c;學學nginx發現還不錯&#xff0c;速度還可以&#xff0c;但是CSS JS確無法使用&#xff0c;原來Iginx配置時需要對不同類型的文件配置規則&#xff0c;真是很郁悶&#xff0c;不過想想也還是很有道理。閑暇之際&#xff0c;把配置貼上來。#user nobody…

五、庫存查詢功能的完善

一、數據庫的建立 由于查詢功能和之前的 入庫管理功能 所用的數據庫都一樣&#xff0c;這里仍使用yy_textile表 在fiber_yy數據庫下創建yy_textile表 初始數據庫信息 二、頁面的完善 登錄注冊頁面我就不演示了&#xff0c;前幾篇博文也都有介紹 query查詢頁面 main_page…