c++中的類型識別

来源:https://www.cnblogs.com/nbk-zyc/archive/2020/03/13/12466779.html
-Advertisement-
Play Games

靜態類型和動態類型、類型虛函數與多態、typeid、dynamic_cast、static_cast關鍵字的使用場合 ...


1、類型識別的相關概念

(1)類型識別的作用

  類型識別是面向對象中引入的一個新概念,主要用來判斷賦值相容性原則中的類型問題,即此時的數據類型到底是基類類型還是派生類類型?

  當基類指針指向子類對象  或者 基類引用成為子類對象的別名  時,就需要使用類型識別;

1 Base *p = new Derived();
2 Base &r = *p

       對於上面的語句,我們可以這樣認識,指針p是Base類型,但是P 又指向了一個新的Derived類型,此時很難判斷指針P 的數據類型;同理,引用r 本來作為父類的別名而存在,但由於賦值相容性,引用r也可以作為子類的別名,同樣此時 引用 r 的數據類型也不能確定;

  註:1)由之前所學知識,若沒有虛函數重寫,編譯器為了安全起見,會將指針p 當作 Base 類型;(編譯期間)    

    2)若有虛函數重寫,就會發生動態多態特性,此時就會根據指針p 所指向的具體數據類型來確定指針p 的數據類型。(運行期間)

(2)類型識別的分類

    

  1)靜態類型:變數(對象)自身的類型;在編譯階段就能確定所使用變數的數據類型。

     2)動態類型:指針(引用)所指向對象的實際類型;在運行階段根據指針所指向的具體數據類型來確定所使用的數據類型。

    

    Base *b 所指向的實際對象無法確定,若指針b 指向的是子類對象,則程式正常運行;若指針b 指向的是父類對象,則程式有可能出現 Bug;

    註:在 g++ 編譯器下上述情況均可正常運行,但後者不建議使用;

   在賦值相容原則中,基類指針是否可以強制類型轉換為子類指針取決於動態類型;(很重要!!!)--- 只有動態類型是子類對象才能進行合法轉換

2、如何得到動態類型

(1)利用多態

  1)必須從基類開始提供類型虛函數;

  2)所有的派生類都必須重寫類型虛函數;

  3)每個派生類的類型 ID必須唯一;

   結果:調用類型虛函數就可以知道當前的對象究竟是什麼類型,這樣就可以得到動態類型,達到動態類型識別效果;

 1 #include <iostream>
 2 #include <string>
 3 
 4 using namespace std;
 5 
 6 class Base
 7 {
 8 public:
 9     enum { ID = 0 };
10     
11     virtual int type()  // 類型虛函數
12     {
13         return ID;
14     }
15 };
16  
17 class Derived : public Base
18 {
19 public:
20     enum { ID = 1 };
21     
22     int type()
23     {
24         return ID;
25     }
26     
27     void print()
28     {
29         cout << "I'm a Derived. " << endl;
30     }
31 };
32 
33 class Child : public Base
34 {
35 public:
36     enum { ID = 2 };
37     
38     int type()
39     {
40         return ID;
41     }
42 };
43  
44 void test(Base* pb)
45 {
46     if( pb->type() == Child::ID )
47     {
48         Child* pc = static_cast<Child*>(pb);
49         //Child* pc = dynamic_cast<Child*>(pb);   // 同上
50         
51         cout << "& = " << pc << endl;
52         cout << "I'm a Child. " << endl;
53     }
54     
55     if( pb->type() == Derived::ID )
56     {
57         Derived* pd = static_cast<Derived*>(pb);
58         //Derived* pd = dynamic_cast<Derived*>(pb); // 同上
59         
60         cout << "& = " << pd << endl;
61         pd->print();
62     }
63     
64     if( pb->type() == Base::ID )
65     {
66         cout << "& = " << pb << endl;
67         cout << "I'm a Base. " << endl;
68     }
69 }
70  
71 int main(int argc, char *argv[])
72 {
73     Base b;
74     Derived d;
75     Child c;
76     
77     test(&b);
78     test(&d);
79     test(&c);
80     
81     return 0;
82 }
83 /**
84  * 運行結果:
85  * & = 0x7ffccf0dd850
86  * I'm a Base. 
87  * & = 0x7ffccf0dd860
88  * I'm a Derived.
89  * & = 0x7ffccf0dd870
90  * I'm a Child.
91  */
利用類型虛函數實現類型識別

 

 (2)利用 dynamic_cast

  1)dynamic_cast這個關鍵字如果要轉換的實際類型和指定的類型不一樣,則會返回NULL。例如當指定類型為子類對象時,如果父類指針的動態類型是這個子類對象時,轉換成功,而動態類型是父類對象或者其他子類對象時,轉換失敗;

  2)dynamic_cast 要求使用的目標對象類型必須是多態,即:所在類族至少有一個虛函數;

  3)只能用於指針和引用之間的轉換

    1. 用於指針轉換時,轉換失敗,返回空指針;

    2. 用於引用轉換時,轉換失敗,將引發 bad_cast異常。

 1 #include <iostream>
 2 #include <string>
 3 
 4 using namespace std;
 5 
 6 class Base
 7 {
 8 public:
 9     virtual ~Base()
10     {
11     
12     }
13 };
14  
15 class Derived : public Base
16 {
17 public:    
18     void print()
19     {
20         cout << "I'm a Derived. " << endl;
21     }
22 };
23 
24 class Child : public Base
25 {
26 
27 };
28  
29 void test(Base* pb)
30 {
31     // dynamic_cast 只能確定最終的轉化結果,無法獲取動態類型的原型
32     Derived* pd = dynamic_cast<Derived*>(pb);
33     
34     if(pd != NULL)
35     {
36         // Derived 類類型, 可以使用指針pd訪問Derived類的成員
37         cout << "& = " << pd << endl;
38         pd->print();
39     }
40     else
41     {
42         Child* pc = dynamic_cast<Child*>(pb);
43         
44         if(pc != NULL)
45         {
46             // Child 類類型, 可以使用指針pc訪問Child類的成員
47             cout << "& = " << pc << endl;
48             cout << "I'm a Child. " << endl;
49         }
50         else
51         {
52             // Base 類類型, 可以使用指針pb訪問Base類的成員
53             cout << "& = " << pc << endl; 
54             cout << "I'm a Base. " << endl;
55         }
56     }
57 }
58  
59 int main(int argc, char *argv[])
60 {
61     Base b;
62     Derived d;
63     Child c;
64     
65     test(&b);
66     test(&d);
67     test(&c);
68     
69     return 0;
70 }
71 /**
72  * 運行結果:
73  * & = 0
74  * I'm a Base. 
75  * & = 0x7ffccf0dd860
76  * I'm a Derived.
77  * & = 0x7ffccf0dd870
78  * I'm a Child.
79  */
利用 dynamic_cast 實現類型識別

 

 (3)利用 typeid(推薦這種方法)

  1)typeid 是一個關鍵字,專門用於動態類型識別;

  2)typeid 關鍵字返回對應參數的類型信息,此類型信息是一個type_info類對象;

    1. 當參數為類型時,返回靜態類型信息;

    2. 當參數為變數時:1>   參數變數內部不存在虛函數表時,返回靜態類型信息;    2>   參數變數內部存在虛函數表時,返回動態類型信息;

    3. 當參數為 NULL 時,將拋出異常;

    3)typeid  使用時需要包含頭文件<typeinfo>;

  4)typeid  使用時直接指定對象或者類型。

  5)typeid 在不同的編譯器內部實現是不同的;

1 int i = 0;
2        
3 const type_info& tiv = typeid(i);  // 將 i 的類型信息放到 type_info 中去;
4 const type_info& tii = typeid(int);
5        
6 cout << (tiv == tii) << endl;  // 1

 

  1 #include <iostream>
  2 #include <string>
  3 #include <typeinfo>
  4 
  5 using namespace std;
  6 
  7 class Base
  8 {
  9 public:
 10     virtual ~Base()
 11     {
 12     }
 13 };
 14  
 15 class Derived : public Base
 16 {
 17 public:
 18     void print()
 19     {
 20         cout << "I'm a Derived." << endl;
 21     }
 22 };
 23  
 24 class Child : public Base 
 25 {
 26 public:
 27     void print()
 28     {
 29         cout << "I'm a Child." << endl;
 30     }
 31 };
 32  
 33 void test(Base* pb)
 34 {
 35     const type_info& tb = typeid(*pb);
 36     
 37     if( tb == typeid(Derived) )
 38     {
 39         Derived* pd = dynamic_cast<Derived*>(pb);
 40     
 41         cout << "& = " << pd << endl;
 42         pd->print();
 43     }
 44     else if( tb == typeid(Child) )
 45     {
 46         Child* pc = dynamic_cast<Child*>(pb);
 47         
 48         cout << "& = " << pc << endl;
 49         pc->print();
 50         
 51     }
 52     else if( tb == typeid(Base) )
 53     {
 54         cout << "& = " << pb << endl;
 55         cout << "I'm a Base. " << endl;
 56     }
 57     
 58     cout << tb.name() << endl;
 59 }
 60  
 61 int main(int argc, char *argv[])
 62 {
 63     Base b;
 64     Derived d;
 65     Child c;
 66     int index;
 67     char ch;
 68     
 69     const type_info& tp = typeid(b);
 70     const type_info& tc = typeid(d);
 71     const type_info& tn = typeid(c);
 72     const type_info& ti = typeid(index);
 73     const type_info& tch = typeid(ch);
 74     
 75     cout<<tp.name()<<endl;
 76     cout<<tc.name()<<endl;
 77     cout<<tn.name()<<endl;
 78     cout<<ti.name()<<endl;
 79     cout<<tch.name()<<endl;
 80     
 81     test(&b);
 82     test(&d);
 83     test(&c);
 84     
 85     return 0;
 86 }
 87 /**
 88  * 運行結果:
 89  * 4Base
 90  * 7Derived
 91  * 5Child
 92  * i
 93  * c
 94  * & = 0x7ffcbd4d6280
 95  * I'm a Base. 
 96  * 4Base
 97  * & = 0x7ffcbd4d6290
 98  * I'm a Derived.
 99  * 7Derived
100  * & = 0x7ffcbd4d62a0
101  * I'm a Child.
102  * 5Child
103  */ 
利用 typeid 實現類型識別

     3 種動態類型的實現方法 建議選 第3種 (typeid)。

  對於多態實現,存在以下缺陷:

    1)必須從基類開始提供類型虛函數;

         2)所有的派生類都必須重寫類型虛函數;

         3)每個派生類的類型名必須唯一;

  對於 dynamic_cast 實現,只能得到類型轉換的結果,不能獲取真正的動態類型,同時 dynamic_cast 必須多態實現。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1. 下載、安裝 下載地址:https://nodejs.org/en/ 安裝:和安裝其他軟體一樣 2. 查看 npm 配置 通過 npm config list 命令查看配置 3. 配置 # 設置NPM prefix目錄,即本地庫目錄 npm config set prefix "D:/npm_r ...
  • 我本身的專業是做電子的,因為這個行業的工資挺低的,就算熬個十年的時間,估計也很難工資過萬,而且基本是沒什麼發展的,都是在國企裡面,沒意思,如果沒有關係,根本別想翻身,但是因為還年輕不想一直這樣下去,所以就聽說了這個web前端,當時還不怎麼火,競爭也不大,但是聽說以後會發展的很好,所以就開始學了,當初 ...
  • 在JavaScript中,函數是經常用到的,在實際開發的時候,我想很多人都沒有太在意函數的聲明與函數表達式的區別,但是呢,這種細節的東西對於學好js是非常重要的。 函數聲明與函數表達式用代碼寫出來是這樣的: //函數聲明 function say(){ console.log("函數聲明") } / ...
  • Node.js Domain(域) 簡化非同步代碼的異常處理,可以捕捉處理try catch無法捕捉的異常。 Domain 模塊可分為隱式綁定和顯式綁定: 隱式綁定: 把在domain上下文中定義的變數,自動綁定到domain對象 顯式綁定: 把不是在domain上下文中定義的變數,以代碼的方式綁定到 ...
  • 程式名稱:功夫滑鼠KongFuMouse聯繫郵箱:[email protected]工程版本:Ver0.1.9版本狀態:工程版本,尚未發佈,敬請期待! 軟體介紹: 還在為一些固定化滑鼠點擊煩惱?還在為頻繁點擊滑鼠點擊煩惱?功夫滑鼠解放您的雙手!!!功夫滑鼠2020是一款功能強大的滑鼠自動執行軟體(滑鼠自動 ...
  • 從程式員往架構師轉型的路上,蔡學鏞老師總結的“四維架構設計方法論”對我頗有幫助,讓我對架構設計有了更加立體化、系統化的認知,現將學習心得分享出來給需要的小伙伴參考。這套方法論通過空間(X、Y、Z)三個維度及時間T維度將問題域解構成可以輕鬆應對的小方塊,分而治之。同時,空間(X、Y、Z)三個維度聯動,... ...
  • 一、動態HTML 1.爬蟲跟反爬蟲 2.動態HTML連載 (1)JavaScript (2)jQuery (3)Ajax (4)DHTML (5)Python採集動態數據 從JavaScript代碼入手採集​;Python第三方庫運行JavaScript,直接採集你在瀏覽器中看到的頁面 二、Sele ...
  • [TOC] getattr詳解 前言 這兩天在優化騰訊雲遷移平臺( "SmartMS" )的中間件( )時. 其中某些介面由於涉及多種伺服器系統類型, 遷移類型的判斷.導致往往一個介面動輒70 80行. 隨便進行一個介面的修改, 調試, 參數的變更. 都將花費好幾分鐘的時間去縷縷中間的邏輯.加上同一 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...