十(1). 強制類型轉換

繼第十部分C++強制類型轉換的四種方式,再進行強化鞏固一下知識點

static_cast 最常用的,在指針之間做轉換

const_cast 去除常量屬性

dynamic_cast 用在基類和派生類之間的轉換

reinterpret_cast 在任意類型之間進行轉

10.1 static_cast

常見的使用場景:

  1. 基本數據類型之間的轉換

    static_cast 可以用于將基本數據類型(如 intfloatchar 等)之間進行轉換。

int i = 10;
float f = static_cast<float>(i); // 將 int 轉換為 float
? 2. 指針或引用類型之間的轉換
  • 如果有指向基類和派生類的指針或引用,static_cast 可以用于指針或引用的類型轉換。在類的層次結構中進行轉換時,static_cast 主要用于向上或向下轉換。

  • 向上轉換:基類指針可以安全地轉換為派生類指針(如果沒有虛函數等特殊情況)。

  • 向下轉換:派生類指針可以轉換為基類指針,但如果不確認對象的實際類型,可能會產生不安全的轉換。為了保證安全性,可以使用 dynamic_cast 進行運行時類型檢查。

class Base {
public:virtual void show() { std::cout << "Base\n"; }
};class Derived : public Base {
public:void show() override { std::cout << "Derived\n"; }
};Base* basePtr = new Derived();
Derived* derivedPtr = static_cast<Derived*>(basePtr); // 向下轉換
derivedPtr->show(); // 調用 Derived 的 show 方法
? 3.??void* 指針轉換為具體類型的指針

void* 是通用指針類型,static_cast 可以將其轉換為具體的指針類型。

void* ptr = malloc(sizeof(int));
int* intPtr = static_cast<int*>(ptr); // 將 void* 轉換為 int* 類型
*intPtr = 100;

4.轉換為枚舉類型

static_cast 可以用于將整數類型轉換為枚舉類型。

enum Color { Red, Green, Blue };int colorCode = 1;
Color color = static_cast<Color>(colorCode); // 將整數轉換為枚舉類型
5.?去除掉常量/volatile 屬性

static_cast 也可以用于去除類型的 constvolatile 屬性,但這通常需要確保你沒有破壞對象的常量性。

const int i = 10;
int* ptr = static_cast<int*>(const_cast<int*>(&i)); // 去除 const 屬性
6.?轉換為 nullptr_t(空指針類型)

可以將指針轉換為 nullptr_t 類型(通常不常用)。

int* ptr = nullptr;
nullptr_t nullPtr = static_cast<std::nullptr_t>(ptr);  // 顯式轉換為空指針類型

// static_cast 是 C++ 中的一種類型轉換操作符,用于在編譯時進行類型轉換。它通常用于在不同類型之間進行顯式轉換,特別是當你知道轉換是安全的時。static_cast 適用于大多數常見的類型轉換,比如基本類型之間的轉換、類層次結構中的轉換等。

10.2 dynamic_cast

dynamic_cast 是 C++ 中用于在類層次結構中進行安全的類型轉換操作符。它與 static_cast 不同,dynamic_cast 主要用于執行運行時類型檢查,尤其在涉及類繼承關系的轉換時,確保轉換是安全的。

dynamic_cast 主要用于:

  1. 將基類指針或引用轉換為派生類指針或引用(通常是向下轉換),并進行運行時檢查。

  2. 用于多態類型,即類具有虛函數時。

基本語法

dynamic_cast<目標類型>(表達式)
  • 目標類型:你希望轉換成的類型,通常是指向派生類的指針或引用。

  • 表達式:需要轉換的表達式,可以是基類指針或引用。

特性:

  1. 運行時類型檢查dynamic_cast 在運行時會檢查對象的實際類型。如果轉換不合法,它將返回 nullptr(對于指針轉換),或者拋出 std::bad_cast 異常(對于引用轉換)。

  2. 僅適用于有虛函數的類dynamic_cast 依賴于 RTTI(運行時類型信息),因此只能在含有虛函數的類上使用。

使用場景:

  1. 向下轉換(派生類指針轉換為基類指針): 這是最常見的情況,通常是基類指針或引用需要轉換為派生類指針或引用。為了安全起見,我們可以使用 dynamic_cast

  2. 安全地進行多態類型轉換: 如果你有一個多態類(具有虛函數的類),你可以使用 dynamic_cast 來確保對象的實際類型與你想要轉換的類型相匹配。

示例 1:指針類型的轉換
#include <iostream>class Base {
public:virtual void speak() { std::cout << "Base speaks\n"; }  // 虛函數
};class Derived : public Base {
public:void speak() override { std::cout << "Derived speaks\n"; }
};int main() {Base* basePtr = new Derived();  // 基類指針指向派生類對象// 使用 dynamic_cast 轉換為派生類指針Derived* derivedPtr = dynamic_cast<Derived*>(basePtr);  // 向下轉換if (derivedPtr) {derivedPtr->speak();  // 輸出 "Derived speaks"} else {std::cout << "Failed to cast to Derived.\n";}delete basePtr;return 0;
}
示例 2:引用類型的轉換
#include <iostream>
#include <stdexcept>class Base {
public:virtual void speak() { std::cout << "Base speaks\n"; }  // 虛函數
};class Derived : public Base {
public:void speak() override { std::cout << "Derived speaks\n"; }
};int main() {Base& baseRef = Derived();  // 基類引用指向派生類對象try {// 使用 dynamic_cast 轉換為派生類引用Derived& derivedRef = dynamic_cast<Derived&>(baseRef);  // 向下轉換derivedRef.speak();  // 輸出 "Derived speaks"} catch (const std::bad_cast& e) {std::cout << "Bad cast: " << e.what() << std::endl;}return 0;
}
示例 3:向上轉換的安全性(dynamic_caststatic_cast 的區別)

dynamic_cast 也可以用于向上轉換(從派生類指針轉換為基類指針)。與 static_cast 不同,dynamic_cast 會進行運行時檢查。對于向上轉換,它的作用不明顯,因為向上轉換通常是安全的,但 dynamic_cast 仍然是有效的。

#include <iostream>class Base {
public:virtual void speak() { std::cout << "Base speaks\n"; }
};class Derived : public Base {
public:void speak() override { std::cout << "Derived speaks\n"; }
};int main() {Derived* derivedPtr = new Derived();// 向上轉換:從 Derived* 轉換為 Base*Base* basePtr = dynamic_cast<Base*>(derivedPtr);  // 向上轉換安全if (basePtr) {basePtr->speak();  // 輸出 "Derived speaks"}delete derivedPtr;return 0;
}

關鍵點:

  1. dynamic_cast 對指針的行為

    • 如果轉換成功,返回指向目標類型的指針。

    • 如果轉換失敗,返回 nullptr

Base* basePtr = new Base();
Derived* derivedPtr = dynamic_cast<Derived*>(basePtr);  // 轉換失敗
if (!derivedPtr) {std::cout << "Conversion failed\n";  // 輸出 "Conversion failed"
}

?2. dynamic_cast 對引用的行為

  • 如果轉換成功,返回目標類型的引用。

  • 如果轉換失敗,會拋出 std::bad_cast 異常。

Base& baseRef = *new Base();
try {Derived& derivedRef = dynamic_cast<Derived&>(baseRef);  // 轉換失敗
} catch (const std::bad_cast& e) {std::cout << "Bad cast exception: " << e.what() << std::endl;  // 輸出異常信息
}

注意事項:

  1. 多態性要求dynamic_cast 只有在類層次結構中的基類至少有一個虛函數時才有效。沒有虛函數的類沒有運行時類型信息(RTTI),因此無法進行類型檢查。

  2. 效率dynamic_cast 需要運行時進行類型檢查,因此它的效率相對較低,尤其是在深層次的類層次結構中,使用時需要考慮性能。

  3. static_cast 的區別

    • static_cast 在編譯時進行類型轉換,沒有運行時檢查,因此沒有運行時開銷,適用于你能保證轉換合法的情況。

    • dynamic_cast 在運行時進行類型檢查,適用于你不確定轉換是否合法的情況。

  4. 不能轉換非類類型dynamic_cast 只能用于類之間的指針或引用轉換,不能用于非類類型(如基本數據類型、數組等)。

總結:

  • dynamic_cast 是 C++ 中用于安全類型轉換的操作符,特別適用于帶有虛函數的類的類型轉換。

  • 它在運行時進行類型檢查,可以避免不安全的轉換,確保程序的安全性。

  • 主要用于多態性強的類層次結構中,幫助我們判斷對象的實際類型并進行適當的轉換。

10.3 einterpret_cast

einterpret_cast 是 C++ 中的另一種強制類型轉換操作符,它與其他類型轉換(如 static_castdynamic_cast)相比,具有不同的特點。reinterpret_cast 可以用來在不同類型之間進行低級別的位級別轉換,即將某種類型的指針或引用轉換為另一種不相關類型的指針或引用。

reinterpret_cast 的基本概念

reinterpret_cast 可以用來執行幾乎任意的指針類型轉換,不管這些類型之間是否有關聯。這意味著你可以將一個指針轉換為另一個完全不相關的類型。例如,將一個 int* 轉換為 float*,或者將 void* 轉換為任何其他類型的指針。

然而,reinterpret_cast 是一種非常危險的類型轉換,因為它直接操作內存,并且沒有運行時檢查。因此,除非你非常清楚你正在做什么,否則應盡量避免使用 reinterpret_cast,因為它可能導致未定義行為。

語法:

reinterpret_cast<目標類型>(表達式)
  • 目標類型:你希望轉換成的類型,通常是指針類型或引用類型。

  • 表達式:你要進行轉換的表達式,通常是指針或引用。

reinterpret_cast 的特性:

  1. 無類型安全reinterpret_cast 不會做任何的類型檢查,它直接處理底層位表示,這可能會導致未定義行為,尤其是在轉換不兼容類型時。

  2. 不涉及類型層次結構:它不關心源類型和目標類型之間的繼承關系。即使它們之間沒有任何直接關系,也可以進行轉換。

  3. 轉換指針和引用類型:它通常用于指針或引用類型之間的轉換,可以將任意類型的指針轉換為任意其他類型的指針。

  4. 可以用于地址運算reinterpret_cast 允許進行非常底層的指針轉換,甚至可以用它將一個 char* 轉換為 int*,或相反,這在一些低級編程中可能有用。

示例代碼:

示例 1:指針類型的轉換
#include <iostream>int main() {int a = 10;// 將 int* 轉換為 char*(這通常是危險的,因為它們的內存布局不同)char* ptr = reinterpret_cast<char*>(&a);// 輸出轉換后的指針地址和值std::cout << "Address of 'a' as char*: " << static_cast<void*>(ptr) << std::endl;// 注意:通過 char* 來訪問 int 的值通常是未定義行為std::cout << "Accessing int through char*: " << static_cast<int>(*ptr) << std::endl;return 0;
}

這個例子展示了如何使用 reinterpret_castint* 轉換為 char*。這種類型轉換雖然在編譯時合法,但通常是不可取的,因為它可能會引發未定義行為,尤其是訪問轉換后的內存時。

示例 2:轉換不同類型的指針
#include <iostream>class A {
public:virtual void speak() { std::cout << "Class A speaking\n"; }
};class B {
public:virtual void greet() { std::cout << "Class B greeting\n"; }
};int main() {A a;B* b = reinterpret_cast<B*>(&a);  // 將 A* 轉換為 B*,這兩者沒有直接關系// 此時,b 可能無法正常工作,訪問會引發未定義行為b->greet();  // 這是未定義行為,因為 A 類沒有 greet 方法return 0;
}

在這個例子中,AB 是沒有任何關系的兩個類,但是我們強行將 A* 轉換為 B*。這將導致未定義行為,通常你不應該在沒有明確知道內存布局的情況下使用這種轉換。

示例 3:轉換整數類型指針
#include <iostream>int main() {int a = 42;void* ptr = reinterpret_cast<void*>(&a);  // 將 int* 轉換為 void*std::cout << "Address of 'a' as void*: " << ptr << std::endl;// 將 void* 轉換回 int* 并訪問值int* intPtr = reinterpret_cast<int*>(ptr);std::cout << "Value of 'a' via int*: " << *intPtr << std::endl;  // 輸出 42return 0;
}

這個例子展示了如何使用 reinterpret_castint* 轉換為 void*,然后再轉換回 int*。這在需要進行內存操作時可能是有用的。

reinterpret_cast 的危險性:

  1. 不類型安全reinterpret_cast 完全繞過了類型系統的檢查,它不會進行任何內存布局的驗證。例如,你可以將一個類型的指針轉換為另一個完全不相關類型的指針,但訪問該指針時可能會導致未定義行為。

  2. 對齊問題:某些硬件平臺要求特定類型的指針具有特定的內存對齊。如果你錯誤地將一個指針轉換為不符合其原始類型要求的類型(例如將一個 int* 轉換為 char*),可能會導致程序崩潰或性能下降。

  3. 內存布局差異:不同類型的對象在內存中的布局可能不同。reinterpret_cast 會直接操作內存,可能會導致訪問數據時出錯,特別是在涉及不同類型的類或數據結構時。

  4. 無運行時檢查:與 dynamic_cast 不同,reinterpret_cast 完全依賴于編譯器,并且不進行運行時檢查。轉換后,如果你訪問轉換后的數據類型,結果完全依賴于你轉換時是否正確理解內存布局。

何時使用 reinterpret_cast

  • 內存操作:在某些底層的編程中(如操作系統開發、硬件驅動、嵌入式系統等),你可能需要直接操作內存,進行類型的位級轉換。這時可以使用 reinterpret_cast

  • 字節流處理:如果你正在處理原始的字節流(如網絡協議解析或文件格式處理),可能需要將某種類型的指針轉換為 void* 或其他類型。

總結:

  • reinterpret_cast 是 C++ 中最強大、最危險的類型轉換操作符。

  • 它允許你進行幾乎所有類型之間的轉換,但不進行類型安全檢查。

  • 使用 reinterpret_cast 時要非常小心,確保你理解底層內存布局和類型轉換的后果。

  • 它通常用于低級編程,如與硬件直接交互、實現自定義內存管理、處理字節流等場景,但在大多數應用程序中不建議使用。

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

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

相關文章

Git版本控制工具詳解

如何區分開發環境和生產環境呢 答案就是寫不同的配置文件&#xff0c;開發的設置成開發需要的&#xff0c;生產的設置成生產需要的&#xff0c;共同放到config這個配置文件夾下面&#xff0c;開發和生成的時候分別加載不同的配置文件 方式二就是使用相同的一個入口配置文件&a…

反向傳播的核心是什么:計算損失函數對可訓練參數的梯度=== 損失函數能通過計算圖連接到可訓練參數

反向傳播的核心是什么:計算損失函數對可訓練參數的梯度 損失函數能通過計算圖連接到可訓練參數 在深度學習中,反向傳播的核心是計算損失函數對可訓練參數的梯度,從而更新這些參數。對于LLM(大型語言模型)而言,是否需要“LLM輸出的參數”才能進行反向傳播 一、反向傳播…

KINGCMS被入侵

現象會強制跳轉到 一個異常網站,請掉截圖代碼. 代碼中包含經過混淆處理的JavaScript&#xff0c;它使用了一種技術來隱藏其真實功能。代碼中使用了eval函數來執行動態生成的代碼&#xff0c;這是一種常見的技術&#xff0c;惡意腳本經常使用它來隱藏其真實目的。 這段腳本會檢…

深入探索串的高級操作:從算法到 LeetCode 實戰

串是編程中最常用的數據結構之一&#xff0c;從簡單的文本處理到復雜的文本匹配算法&#xff0c;串的應用無處不在。在掌握了串的基本概念、存儲結構以及KMP算法之后&#xff0c;現在讓我們深入探索串的更多高級操作&#xff0c;例如求子串、串的替換等&#xff0c;并通過LeetC…

在rocky linux 9.5上在線安裝 docker

前面是指南&#xff0c;后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …

OneNet + openssl + MTLL

1.OneNet 使用的教程 1.在網絡上搜索onenet&#xff0c;注冊并且登錄賬號。 2.產品服務-----物聯網服務平臺立即體驗 3.在底下找到立即體驗進去 4.產品開發------創建產品 5.關鍵是選擇MQTT&#xff0c;其他的內容自己填寫 6.這里產品以及開發完成&#xff0c;接下來就是添加設…

【Fiddler工具判斷前后端Bug】

Fiddler工具判斷前后端Bug的方法 使用Fiddler抓包工具可以高效定位問題是出在前端還是后端&#xff0c;主要通過分析請求和響應的內容、狀態碼、數據格式等關鍵信息。 分析請求是否成功發送 檢查請求是否從客戶端正確發出&#xff0c;觀察Fiddler抓取的請求列表。若請求未出…

【論文閱讀筆記】《A survey on deep learning approaches for text-to-SQL》

文章目錄 一、論文基本信息1. 文章標題2. 所屬刊物/會議3. 發表年份4. 作者列表5. 發表單位 二、摘要三、解決問題四、創新點五、自己的見解和感想六、研究背景七、研究方法&#xff08;模型、實驗數據、評估指標&#xff09;八、總結&#xff08;做了什么、得到了什么、有什么…

【強連通分量 縮點 最長路 拓撲排序】P2656 采蘑菇|普及+

本文涉及知識點 C圖論 強連通分量 縮點 最長路 拓撲排序 P2656 采蘑菇 題目描述 小胖和 ZYR 要去 ESQMS 森林采蘑菇。 ESQMS 森林間有 N N N 個小樹叢&#xff0c; M M M 條小徑&#xff0c;每條小徑都是單向的&#xff0c;連接兩個小樹叢&#xff0c;上面都有一定數量的…

Dubbo Logback 遠程調用攜帶traceid

背景 A項目有調用B項目的服務&#xff0c;A項目使用 logback 且有 MDC 方式做 traceid&#xff0c;調用B項目的時候&#xff0c;traceid 沒傳遞過期&#xff0c;導致有時候不好排查問題和鏈路追蹤 準備工作 因為使用的是 alibaba 的 dubbo 所以需要加入單獨的包 <depend…

nodejs:用 nodemailer 發送一封帶有附件的郵件

我們將使用 nodemailer 庫來發送帶有附件的郵件。 首先&#xff0c;確保已經安裝了nodemailer。如果沒有安裝&#xff0c;可以通過 npm install nodemailer 來安裝。 cnpm install nodemailer --save dependencies: – nodemailer ^7.0.3 步驟&#xff1a; 引入nodemailer模…

Scade 語言概念 - 方程(equation)

在 Scade 6 程序中自定義算子(Operator)的定義、或數據流定義(data_def)的內容中&#xff0c;包含一種基本的語言結構&#xff1a;方程(equation)(注1)。在本篇中&#xff0c;將敘述 Scade 語言方程的文法形式&#xff0c;以及作用。 注1: 對 Scade 中的 equation, 或 equation…

STM32開發,創建線程棧空間大小判斷

1. 使用RTOS提供的API函數&#xff08;以FreeRTOS為例&#xff09; 函數原型&#xff1a;UBaseType_t uxTaskGetStackHighWaterMark(TaskHandle_t xTask)功能&#xff1a;獲取指定任務堆棧中剩余的最小空間&#xff08;以字為單位&#xff0c;非字節&#xff09;。使用步驟&am…

thinkphp8.1 調用巨量廣告API接口,刷新token

1、在mysql中建立表sys_token; CREATE TABLE sys_token (id int UNSIGNED NOT NULL,access_token varchar(50) COLLATE utf8mb4_general_ci NOT NULL,expires_in datetime NOT NULL,refresh_token varchar(50) COLLATE utf8mb4_general_ci NOT NULL,refresh_token_expires_in …

【leetcode】遞歸,回溯思想 + 巧妙解法-解決“N皇后”,以及“解數獨”題目

&#x1f4da;?前言 &#x1f31f; 本期內容亮點&#xff1a;我們將深入解析力扣&#xff08;LeetCode&#xff09;上的幾道經典算法題&#xff0c;涵蓋不同難度和題型&#xff0c;幫助大家掌握解題思路和代碼實現技巧。無論是準備面試還是提升算法能力&#xff0c;這些題解都…

【iOS安全】iPhone X iOS 16.7.11 (20H360) WinRa1n 越獄教程

前言 越獄iPhone之后&#xff0c;一定記得安裝一下用于屏蔽更新的描述文件&#xff08;可使用愛思助手&#xff09; 因為即便關閉了自動更新&#xff0c;iPhone仍會在某些時候自動更新系統&#xff0c;導致越獄失效&#xff1b;更為嚴重的是&#xff0c;更新后的iOS版本可能是…

??高頻通信與航天電子的材料革命:獵板PCB高端壓合基材技術解析??

—聚酰亞胺/陶瓷基板在5G與航天場景的產業化應用?? ??一、極端環境材料體系&#xff1a;突破溫域與頻率極限?? ??聚酰亞胺基板&#xff08;PI&#xff09;的航天級穩定性?? 獵板在衛星通信PCB中采用真空層壓工藝處理聚酰亞胺基材&#xff08;Dk≈10.2&#xff09;&a…

pikachu靶場通關筆記13 XSS關卡09-XSS之href輸出

目錄 一、href 1、常見取值類型 2、使用示例 3、安全風險 二、源碼分析 1、進入靶場 2、代碼審計 3、滲透思路 三、滲透實戰 1、注入payload1 2、注入payload2 3、注入payload3 本系列為通過《pikachu靶場通關筆記》的XSS關卡(共10關&#xff09;滲透集合&#xff…

day26-計算機網絡-4

1. tcp的11種狀態 ss -ant -a 表示看所有狀態 -n 表示不將ip解析為主機名 -t 表示tcp 1.1. closed狀態&#xff08;客戶端、服務端&#xff09; 客戶端發起建立連接前的狀態服務端啟動服務前的狀態 1.2. listen狀態&#xff08;服務端&#xff09; 服務端軟件運行的時候狀…

基于autodl部署Cross-Modal-Re-ID-baseline

https://arxiv.org/abs/2001.04193 https://github.com/mangye16/Cross-Modal-Re-ID-baseline/tree/master?tabreadme-ov-file# 需要SYSU-MM01.zip pip install numpy pandas scipy scikit-learn pillow tqdm把SYSU-MM01放到…/Datasets/SYSU-MM01/ori_data下 先運行pytho…