C++11學習 virtual(虛函數)的用法

Virtual虛函數

  • 在面向對象的C++語言中,虛函數(virtual?function)是一個非常重要的概念。因為它充分體現了面向對象思想中的繼承和多態性這兩大特性,在C++語言里應用極廣。
  • 多態性:其含義就是多種形式;將具有繼承關系的多種類型稱之為多態模型,因為使用者可以使用這種類型的多種形式,但是不需要在意他們之間的差異。歸根結底,引用或指針的靜態類型與動態類型的不一致是C++語言支持多態性的根本原因。
  • 在派生類中覆蓋了某一個虛函數,可以再一次使用virtual關鍵字指出該函數的性質。如果將一個函數聲明為虛函數,那么這個函數在所有的派生類別中都是虛函數。一個派生類的函數如果覆蓋了某個繼承而來的虛函數,則它的形參類型必須要和被覆蓋的函數完全一致。同樣,派生類中的虛函數返回的類型必須和基類的類型是一致的,但是這個存在一個例外。當類的虛函數返回的類型是類本身的指針或者引用的時候,上述規則無效。
  • 什么是虛函數呢?虛函數是指一個類中你希望重載的成員函數,當你用一個基類指針或引用指向一個繼承類對象的時候,你調用一個虛函數,實際調用的是繼承類的版本。?——摘自MSDN
#include <cstring>
#include <string>
#include <iostream>
#include <stdio.h>
#include <conio.h>
using namespace std;class Parent{public:char data[20];void Function1();virtual void Function2(); //這里的Function2是虛擬函數}parent;void Parent::Function1() {printf("This is parent,function1\n");
}void Parent::Function2() {printf("This is parent,function2\n");
}class Child:public Parent{void Function1();void Function2();
}child;void Child::Function1(){printf("This is child,function1\n");
}void Child::Function2() {printf("This is child,function2\n");
}
int main(int argc,char* argv[]){Parent *p;//定義了一個基類指針if(_getch() == 'c'){p = &child;  // 如果用戶輸入一個小寫的字母c,指向繼承類對象}else{p = &parent; // 否則指向一個基類對象}p->Function1();  // 這里編譯時會直接給出Parent::Function1()的入口地址p->Function2();  // 注意這里,執行的是哪一個Function2?}

  • 為什么會有第一行的結果呢?因為是用一個Parent類的指針調用函數Fuction1(),雖然實際上這個指針指向的是Child類的對象,但編譯器無法知道這一事實(直到運行的時候,程序才可以根據用戶的輸入判斷出指針指向的對象),它只能按照調用Parent類的函數來理解并編譯,所以看到了第一行的結果。
  • 那么第二行的結果又是怎么回事呢?注意到,Function2()函數在基類中被virtual關鍵字修飾,也就是說,它是一個虛函數。虛函數最關鍵的特點是“動態聯編”,它可以在運行時判斷指針指向的對象,并自動調用相應的函數。
  • 如果在運行上面的程序時任意輸入一個非c的字符,結果如下:

  • 請注意看第二行,它的結果出現了變化。程序中僅僅調用了一個Function2()函數,卻可以根據用戶的輸入自動決定到底調用基類中的Function2還是繼承類中的Function2,這就是虛函數的作用。在MFC中,很多類都是需要繼承的,它們的成員函數很多都要重載,比如編寫MFC應用程序最常用的CView::OnDraw(CDC*)函數,就必須重載使用。把它定義為虛函數(實際上,在MFC中OnDraw不僅是虛函數,還是純虛函數),可以保證時刻調用的是用戶自己編寫的OnDraw。虛函數的重要用途在這里可見一斑。
  • PS:一定要注意“靜態聯翩 ”和“ 動態聯編 ”的區別,對于我來說,若沒有在VC6.0中親自去測試,憑自己的感覺,當在鍵盤中輸入“c”時,因為雖然實際上這個指針指向的是Child類的對象,但編譯器無法知道這一事實,它只能按照調用Parent類的函數來理解并編譯,所以我們看到了第一行的結果。第二行中調用了子類的function2,完全是因為virtual 的功能,virtual實現了動態聯編,它可以在運行時判斷指針指向的對象,并自動調用相應的函數。當然,如果執行的是p=&parent; 這一句,該指針很明顯的是指向父類,那么肯定調用的是父類的方法?

final和override說明符

  • 派生類如果定義了一個函數與基類的虛函數的名字相同但是形參列表不同,這仍然是一個合法的行為,需要使用關鍵字override來說明派生類中的虛函數。
  • 如果將某個函數指定為final,則之后任何嘗試覆蓋該函數的操作都會引發錯誤。

虛函數和默認實參

  • 虛函數使用默認實參,則基類和派生類中定義的默認實參最好一致。

回避虛函數的機制

  • 如果希望對于虛函數的調用不是動態的綁定,而是強迫其執行虛函數的某一個特定的版本。可以使用作用域運算符來實現
  • double undiscounted = baseP->Quote::net_price(43);//強行調用基類中定義的函數版本,而不管baseP的動態類型是什么
  • 通常情況下,只有成員函數(或友元函數)中的代碼才需要使用作用域運算符來回避虛函數的機制。如果一個派生類虛函數需要調用它的基類版本,但是沒有使用作用域運算符,則在運行的時候該調用會被解析成對于派生類的版本自身的調用,從而導致無限的遞歸。

?

?

?

?

?

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

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

相關文章

c++ const

函數名稱不單單是函數名 const 補充內容 還有 const 參數類型 函數后頭const 只能在成員函數如果const對象 但是我沒通過const成員函數 就會報錯

C++11學習 新特性之 “=default” 、“=delete”

文章目錄 1、 default 和delete 概述2、 類與默認函數3、 使用“delete”來限制函數生成4、 “default”使用范圍 1、 default 和delete 概述 default、delete 是C11的新特性&#xff0c;分別為&#xff1a;顯式缺省(告知編譯器生成函數默認的缺省版本)和顯式刪除(告知編譯器…

C++學習 優雅的實現對象到文件的序列化/反序列化 關鍵字serialize

需要使用到序列化場景的需求 在寫代碼的過程中&#xff0c;經常會需要把代碼層面的對象數據保存到文件&#xff0c;而這些數據會以各種格式存儲&#xff0e;例如&#xff1a;json&#xff0c;xml&#xff0c;二進制等等&#xff0e;二進制&#xff0c;相比json&#xff0c;xml…

C++代碼注釋詳解

常用注釋語法 注釋寫在對應的函數或變量前面。JavaDoc類型的多行注釋風格如下&#xff1a; /** * 這里為注釋. */ 一般注釋中有簡要注釋和詳細注釋&#xff0c;簡要注釋有多種標識方式&#xff0c;這里推薦使用brief命令強制說明&#xff0c;例如&#xff1a;/** * brief 這里…

段錯誤:SIGSEGV

SIGSEGV是在訪問內存時發生的錯誤&#xff0c;它屬于內存管理的范疇 SIGSEGV是一個用戶態的概念&#xff0c;是操作系統在用戶態程序錯誤訪問內存時所做出的處理。 當用戶態程序訪問&#xff08;訪問表示讀、寫或執行&#xff09;不允許訪問的內存時&#xff0c;產生SIGSEGV。 …

web3 0.2.x 和 1.x.x版本之間的差異

版本差異 單位轉換 0.2.x web3.fromWei(13144321,ether) 1.x.x web3.utils.fromWei(13144321,ether)1.0以后的版本使用了大量的Promise&#xff0c;可以結合async/await使用&#xff0c;而0.20版本只支持回調函數

如何提高閱讀源碼的能力并且手撕源碼

怎么有效的手撕代碼呢&#xff1f; 把代碼跑起來把代碼一個片段拿出來使用畫出代碼運行的流程圖一行一行的搬運在看源碼的情況下寫出類似的demo

并發和并行的區別簡單介紹

并發和并行 并發是關于正確有效地控制對共享資源的訪問 同時完成多個任務。在開始處理其他任務之前&#xff0c;當前任務不需要完成。并發解決了阻塞發生的問題。當任務無法進一步執行&#xff0c;直到外部環境發生變化時才會繼續執行。最常見的例子是I/O&#xff0c;其中任務…

手撕源碼 alloc

怎么有效的手撕代碼呢&#xff1f; gnu gcc 2.9 的 內存池 把代碼跑起來把代碼一個片段拿出來使用畫出代碼運行的流程圖一行一行的搬運在看源碼的情況下寫出類似的demo 第三步&#xff1a; 第五步: // 這個頭文件包含一個模板類 allocator&#xff0c;用于管理內存的分配、…

Algorand的共識協議及其核心的優勢

Algorand 設計的初衷 Algorand 想解決的核心問題是&#xff1a;去中心化網絡中低延時&#xff08;Latency&#xff09;和高置信度&#xff08;Confidence&#xff09;之間的矛盾。其中&#xff0c;延時指從發起交易到確認交易所需要的時間&#xff1b;置信度指的是發出的交易不…

手撕源碼 SQL解析器 sqlparser

怎么有效的手撕代碼呢&#xff1f; 源代碼&#xff1a;https://github.com/hyrise/sql-parser 把代碼跑起來把代碼一個片段拿出來使用畫出代碼運行的流程圖一行一行的搬運在看源碼的情況下寫出類似的demo

針對Algorand所使用的密碼相關技術細節進行介紹

關鍵概念 VRF: 可驗證隨機函數。簡單來說是&#xff1a;vrf,Proof VRF(sk,seed)&#xff0c;sk為私鑰&#xff0c;seed為隨機種子&#xff1b;通過Verify(proof,pk,seed)驗證vrf的合法性。cryptographic sorition: 根據用戶本輪的VRF值&#xff0c;自身的權重以及公開的區塊鏈…

內存池的實現1 :重載

#ifndef KSTD_ALLOCATOR_H_ #define KSTD_ALLOCATOR_H_// 這個頭文件包含一個模板類 allocator&#xff0c;用于管理內存的分配、釋放&#xff0c;對象的構造、析構 // 暫不支持標準庫容器 todo::支持萃取#include <new> // placement new #include <cstddef>…

對于Algorand的介紹

介紹 Algorand具有能耗低、效率高、民主化、分叉概率極低、可拓展性好等優點&#xff0c;旨在解決現有區塊鏈項目存在的“不可能三角”&#xff08;高度可擴展的、安全的、去中心化&#xff09;問題。Algorand由MIT教授、圖靈獎得主Silvio Micali發起&#xff0c;擁有MIT區塊鏈…

內存池的實現2 類專用的內存適配器

B類增加了嵌入指針 #include<new> #include<ctime> #include<iostream> #include<cstdio> class A { public:A() {printf("next%p\n", next);};static void* operator new(size_t size);static void operator delete(void* phead);static i…

C++學習 高級編程

C 文件和流 到目前為止&#xff0c;目前使用最為廣泛的是 iostream 標準庫&#xff0c;它提供了 cin 和 cout 方法分別用于從標準輸入讀取流和向標準輸出寫入流。以下將介紹從文件讀取流和向文件寫入流。這就需要用到 C 中另一個標準庫 fstream&#xff0c;它定義了三個新的數…

內存池的實現3 固定大小的allocator單線程內存配置器

如果我們想使內存管理器用于其他大小不同的類該怎么辦呢&#xff1f;為每一個類重復管理邏輯顯然是對開發時間的不必要浪費。如果我們看一下前面內存管理器的實現&#xff0c;就會明顯地看出內存管理邏輯實際上獨立于特定的類 有關的是對象的大小一這是內存池模板實現的良好候選…

C++中文版本primer 第二章變量和基本類型 學習筆記

2.2變量 2.2.1 變量定義 列表初始化 定義一個名字為units_sold的int變量并初始化為0 int units_sold 0; int units_sold {0}; int units_sold{0}; int units_sold(0); C11 用花括號來初始化變量&#xff0c;上面這個步驟也稱之為列表初始化。這種初始化有一個重要的特點&…

內存池中的嵌入式指針

嵌入式指針 可以union改struct 內存分配后 next指針就沒用了 直接作為數據空間比較省內存 因為對指針指向的內存存儲的時候 編譯器是不管你是什么類型的 &#xff0c;這里有道練習題可以對指針的概念稍微理解一下&#xff1a; #include <iostream> using std::cout; us…