c++中的類型轉換

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

目錄 1. 隱式類型轉換 2. 顯示類型轉換/強制類型轉換( static_cast、const_cast、reinterpret_cast、dynamic_cast) 3. 類型轉換函數、轉換構造函數 類型轉換可分為 隱式類型轉換(編譯器自動完成) 與 顯示類型轉換(強制類型轉換,需要自己操作)。 ...


目錄

  1. 隱式類型轉換

  2. 顯示類型轉換/強制類型轉換( static_castconst_castreinterpret_castdynamic_cast

  3. 類型轉換函數、轉換構造函數

 

  類型轉換可分為 隱式類型轉換(編譯器自動完成) 與 顯示類型轉換(強制類型轉換,需要自己操作)。

隱式類型轉換  

  基本數據類型之間會進行隱式的類型安全轉換。其轉換規則如下:

  

   我們用 1個案列來介紹這種隱式類型轉換規則,會有意想不到的結果發生....!!!

 1 #include <iostream>
 2 #include <string>
 3 
 4 using namespace std;
 5 
 6 int main()
 7 {   
 8     short s = 'a';
 9     unsigned int ui = 1000;
10     int i = -2000;
11     double d = i;
12     
13     cout << "d = " << d << endl;
14     cout << "ui = " << ui << endl;
15     cout << "ui + i = " << ui + i << endl;
16     
17     if( (ui + i) > 0 )  // 變數i會進行隱式類型轉換 -> unsigned int  // ui + i > 0
18     {
19         cout << "Positive" << endl;
20     }
21     else
22     {
23         cout << "Negative" << endl;
24     }
25     
26     cout << "sizeof(s + 'b') = " << sizeof(s + 'b') << endl;    // 4 // s -> int  'b' -> int // sizeof(s + 'b') = sizeof(int) // 編譯器預設是int運算
27     
28     return 0;
29 }
30 /*
31     d = -2000
32     ui = 1000
33     ui + i = 4294966296
34     Positive
35     sizeof(s + 'b') = 4
36 */
基本類型的隱式類型轉換案列

  在c++中,還有其它幾種隱式類型轉換(後續講解),那麼現在試想一下這幾個問題:

    1. 基本類型 可以轉換為 類類型嗎?--- 可以,見轉換構造函數;

    2. 類類型 可以轉換為 基本類型嗎?--- 可以,見類型轉換函數;

    3. 類類型之間可以轉換嗎?--- 可以,見轉換構造函數 和 類型轉換函數;

  註:這種隱式類型轉換不能夠抑制,並且也是bug的來源之一;

顯示類型轉換/強制類型轉

  在介紹c++強制類型轉換前,我們可以回顧一下c語言中的強制類型轉換。c語言中的強制類型轉換十分簡單、粗暴,即 (Type)(Expression);或者 Type(Expression);但是,這種簡單的強制類型轉換引發了很多問題,可歸納為如下2點:

  1. 任意類型之間都可以進行轉換,編譯器很難判斷其正確性(過於粗暴);

  2. 在源碼中無法快速定位所有使用強制類型轉換的語句(很難定位);

所以,基於這2點考慮,在c++中引入了新式類型轉換( static_castconst_castreinterpret_castdynamic_cast),其使用方法可歸納為 xxx_cast<Type>(Expression)

1、static_cast

  1. 用於 基本類型間的轉換

  2. 不能用於基本類型指針間的轉換

  3. 用於有繼承關係類對象之間的轉換和類指針之間的轉換

  4. 用於 其它類型(基本類型和類類型) 向 類類型之間的轉換;static_cast<類類型>(其它類型),見轉換構造函數。

2、 const_cast

  1. 用於去除變數的只讀屬性

  2. 強制轉換的目標類型必須是指針或引用

3、 reinterpret_cast

  1. 用於指針類型間的強制轉換

  2. 用於整數和指針類型間的強制轉換

4、 dynamic_cast

  1. 用於有繼承關係的類指針(引用)間的轉換

  2. 用於有交叉關係的類指針(引用)間的轉換

  3. 相關類(基類)中必須有虛函數的支持

  4. 具有類型檢查的功能,但類型轉換的結果只可能在運行階段得到;

  指針轉換:

    轉換成功:得到目標類型的指針;

    轉換失敗:得到一個空指針;

  引用轉換: 

    轉換成功:得到目標類型的引用;

    轉換失敗:得到一個異常操作的信息;

  dynamic_cast 轉換時錯誤提示:

    1. 不能將父類指針 直接 轉換為 子類指針

      

        

     2. 在 父類中沒有虛函數,不能發生多態 polymorphic

      

       

 1 #include <stdio.h>
 2 
 3 void static_cast_demo()
 4 {
 5     int i = 0x12345;
 6     char c = 'c';
 7     int* pi = &i;
 8     char* pc = &c;
 9     
10     c = static_cast<char>(i);
11     pc = static_cast<char*>(pi);    // error static_cast 不能用於基本類型指針之間 的轉換
12 }
13 
14 void const_cast_demo()
15 {
16     const int& j = 1;
17     int& k = const_cast<int&>(j);
18     
19     const int x = 2;
20     int& y = const_cast<int&>(x);
21     
22     int z = const_cast<int>(x);     // error const_cast 強制轉換的目標類型必須是指針或引用類型
23     
24     k = 5;
25     
26     printf("k = %d\n", k);  // 5
27     printf("j = %d\n", j);  // 5
28     
29     y = 8;
30     
31     printf("x = %d\n", x);  // 2
32     printf("y = %d\n", y);  // 8
33     printf("&x = %p\n", &x);// 0x7fffd40b84e8
34     printf("&y = %p\n", &y);// 0x7fffd40b84e8
35 }
36 
37 void reinterpret_cast_demo()
38 {
39     int i = 0;
40     char c = 'c';
41     int* pi = &i;
42     char* pc = &c;
43     
44     pc = reinterpret_cast<char*>(pi);
45     pi = reinterpret_cast<int*>(pc);
46     pi = reinterpret_cast<int*>(i);
47     c = reinterpret_cast<char>(i);  // error reinterpret_cast 適用於指針類型之間的轉換  和 整型與指針類型之間的轉換
48 }
49 
50 void dynamic_cast_demo()
51 {
52     int i = 0;
53     int* pi = &i;
54     char* pc = dynamic_cast<char*>(pi); // error
55 }
56 
57 int main()
58 {
59     static_cast_demo();
60     const_cast_demo();
61     reinterpret_cast_demo();
62     dynamic_cast_demo();
63     
64     return 0;
65 }
static_cast、const_cast、reinterpret_cast、dynamic_cast 初體驗

 

 1 #include <iostream>
 2 #include <string>
 3 
 4 using namespace std;
 5 
 6 class Base
 7 {
 8 public:
 9     Base()
10     {
11         cout << "Base::Base()" << endl;
12     }
13     
14     virtual ~Base()
15     {
16         cout << "Base::~Base()" << endl;
17     }
18 };
19 
20 class Derived : public Base
21 {
22 public:
23     Derived()
24     {
25         cout << "Derived::Derived()" << endl;
26     }
27     
28     void func()
29     {
30         cout << "Derived::func()" << endl;
31     }
32     
33     virtual ~Derived()
34     {
35         cout << "Derived::~Derived()" << endl;
36     }
37 };
38 
39 void test1()
40 {
41     Base* bp = new Derived();
42     
43     Derived *dp = dynamic_cast<Derived*>(bp);   // 當父類指針指向的是子類對象,轉換成功
44     //Derived *dp = static_cast<Derived*>(bp);  // 當父類指針指向的是子類對象,轉換成功
45     if( dp != NULL )
46     {
47         cout << "dp = " << dp << endl;
48         dp->func();
49     }
50     else
51     {
52         cout << "Cast error!" << endl;
53     }
54     
55     delete bp;
56 }
57 
58 void test2()
59 {
60     Base* bp = new Base();
61     
62     Derived *dp = dynamic_cast<Derived*>(bp);   // 轉化失敗,不能將父類指針對象轉換為子類指針對象
63     //Derived *dp = static_cast<Derived*>(bp);     // 轉換成功,可以將父類指針對象轉換為子類指針對象
64     if( dp != NULL )
65     {
66         cout << "dp = " << dp << endl;
67         dp->func();
68     }
69     else
70     {
71         cout << "Cast error!" << endl;
72     }
73     
74     delete bp;
75 }
76 
77 int main()
78 {
79     test1();
80     
81     cout << "-----------------------" << endl;
82     
83     test2();
84 
85     return 0;
86 }
static_cast 與 dynamic_cast 測試案列

  通過 《 static_cast 與 dynamic_cast 測試案列 》可知,當使用 dynamic_cast 強制類型轉換時,即 Derived *dp = dynamic_cast<Derived*>(bp); 程式的運行結果為:

          

   當使用 static_cast 強制類型轉換時,即 Derived *dp = static_cast<Derived*>(bp); 程式的運行結果為:

         

  從運行結果可知, static_cast 與 dynamic_cast 在繼承中進行類指針轉換時是存在差異的;其中,

    1. 相同點:當父類指針 指向 子類對象時,二者都可以將父類指針 成功轉換為 子類指針;

      

     2. 不同點:當父類指針 指向 父類對象時,

      1) static_cast 轉換:可以將父類指針 成功轉換為 子類指針;

      2)dynamic_cast 轉換:父類指針 不能夠轉換為  子類指針;

       

類型轉換函數

1、轉換構造函數

  當構造函數只有1個參數 參數的類型是基本類型 或者是 其它類型時,就是轉換構造函數。其作用是將其他類型 轉換為 類類型

  編譯器儘力嘗試的結果是隱式類型轉換,隱式類型轉換是工程中bug的重要來源;

  工程中通過explicit關鍵字杜絕隱式轉換,轉換構造函數被explicit修飾時只能進行顯示轉換;

  轉換方式

  1. static_ cast<ClassName >(value);

  2. ClassName(value);

  3. (ClassName)value; //不推薦

  在類型轉換時調用轉換構造函數。

         

 1 #include <iostream>
 2 #include <string>
 3 
 4 using namespace std;
 5 
 6 class Test
 7 {
 8     int mValue;
 9 public:
10     Test()
11     {
12         mValue = 0;
13     }
14     
15     explicit Test(int i)
16     {
17         mValue = i;
18     }
19     
20     Test operator + (const Test& p)
21     {
22         Test ret(mValue + p.mValue);
23         
24         return ret;
25     }
26     
27     int value()
28     {
29         return mValue;
30     }
31 };
32 
33 int main()
34 {   
35     Test t;
36     Test r;
37     
38     // 隱式類型轉換 不加explicit關鍵字
39     //t = 5;        // t = Test(5);
40     //r = t + 10;   // r = t + Test(10);
41     
42     t = static_cast<Test>(5);    // t = Test(5);    
43     r = t + static_cast<Test>(10);   // r = t + Test(10);
44     
45     cout << r.value() << endl;  // 15
46     
47     return 0;
48 }
轉換構造函數之 基本類型 -> 類類型

 2、類型轉換函數

   c++ 中可以定義類型轉換函數,其作用是將 類類型 轉換為 其它類型;其語法格式為:

1 // 類型轉換函數語法格式
2 operator Type()
3 {
4      Type ret;
5 
6      // ...
7   
8      retuan Type;
9 }

  編譯器能夠隱式的使用類型轉換函數

 

 1 #include <iostream>
 2 #include <string>
 3 
 4 using namespace std;
 5 
 6 class Test
 7 {
 8     int mValue;
 9 public:
10     Test(int i = 0)
11     {
12         mValue = i;
13     }
14     int value()
15     {
16         return mValue;
17     }
18     operator int ()
19     {
20         return mValue;
21     }
22 };
23 
24 int main()
25 {   
26     Test t(100);
27     int i = t;  // 隱式的使用類型轉換函數 operator int ()
28     
29     cout << "t.value() = " << t.value() << endl;    // t.value() = 100
30     cout << "i = " << i << endl;    // i = 100
31     
32     return 0;
33 }
類型轉換函數之 類類型 -> 基本類型

3、類型轉換函數  VS  轉換構造函數 

  結論:

  1. 類型轉換函數 與 轉換構造函數 具有同等的地位

  2. 無法抑制隱式的類型轉換函數調用,此時 類型轉換函數可能與轉換構造函數衝突(類類型之間的轉換);

  3. 在類型轉換時 調用類型轉換函數 、轉換構造函數。

  4. 工程中以Type toType()的公有成員 代替 類型轉換函數

    

 1 #include <iostream>
 2 #include <string>
 3 
 4 using namespace std;
 5 
 6 class Test;
 7 
 8 class Value
 9 {
10 public:
11     Value()
12     {
13     }
14     explicit Value(Test& t)
15     {
16         cout << "explicit Value(Test& t)" << endl;
17     }
18 };
19 
20 class Test
21 {
22     int mValue;
23 public:
24     Test(int i = 0)
25     {
26         mValue = i;
27     }
28     int value()
29     {
30         return mValue;
31     }
32     operator Value()
33     {
34         Value ret;
35         cout << "operator Value()" << endl;
36         return ret;
37     }
38 };
39 
40 int main()
41 {   
42     Test t(100);
43     Value v1 = t;    // 隱式的調用類型轉換函數 operator Value()
44     Value v2 = static_cast<Value>(t);    // 顯示的調用轉換構造函數 explicit Value(Test& t)
45     
46     return 0;
47 }
48 /*
49     運行結果:
50     operator Value()
51     explicit Value(Test& t)
52 */
類類型之間的相互轉換

      //  類型轉換函數與轉換構造函數發生衝突   的示意圖    

           

 


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

-Advertisement-
Play Games
更多相關文章
  • 一、Map常用方法簡介 package com.bjpowernode.java_learning; ​ import java.util.HashMap; import java.util.*; ​ public class D91_1_MapCommonMethod { public stati ...
  • 模型評價是指對於已經建立的一個或多個模型,根據其模型的類別,使用不同的指標評價其性能優劣的過程。常用的聚類模型評價指標有ARI評價法(蘭德繫數)、AMI評價法(互信息)、V-measure評分、FMI評價法和輪廓繫數等。常用的分類模型評價指標有準確率(Accuracy)、精確率(Precision) ...
  • 員工管理系統 因為學業要求,需要完成一個過關檢測,但是因為檢測之前沒有做好準備,且想到之前用mysql+jdbc+Struts2+bootstrap做成了一個ATM系統(主要有對數據的增刪改查操作),應對這次的檢測應該不成問題,但是萬萬沒想到,過關檢測重在“檢測”,需要在規定的時間內完成一個系統,且 ...
  • Python 程式能用很多方式處理日期和時間,轉換日期格式是一個常見的功能。Python 提供了 time ,datatime, calendar 等模塊可以用於格式化日期和時間。時間間隔是以秒為單位的浮點小數。每個時間戳都以自從 1970 年 1 月 1 日午夜(歷元)經過了多長時間來表示。Pyt ...
  • 本文章將要介紹的內容有以下幾點,讀者朋友也可先自行思考一下相關問題: 1. 線程中斷 interrupt 方法怎麼理解,意思就是線程中斷了嗎?那當前線程還能繼續執行嗎? 2. 判斷線程是否中斷的方法有幾個,它們之間有什麼區別? 3. LockSupport的 park/unpark 和 wait/n ...
  • defer 關鍵字 首先來看官網的定義: A "defer" statement invokes a function whose execution is deferred to the moment the surrounding function returns, either because ...
  • 1、運算符 運算符 功能 是否支持 字元串 列表 元組 字典 集合 + 合併 √ √ √ * 複製 √ √ √ in 判斷是否存在 √ √ √ √ √ not in 判斷是否不存在 √ √ √ √ √ 2、公共方法 len() 統計容器中元素的個數 del/del() 刪除 max() 返回容器中元 ...
  • 春節期間熟悉了TP6, 也寫了一個TP6的博客程式,但系統的異常頁面實在另外頭疼,很多時候無法查看到是哪行代碼出的問題。 所以就特別的想把whoops引進來,經過一系列的研究,終於找到瞭解決的辦法: 1. 通過composer安裝whoops 運行命令: composer require filp/ ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...