C語言隱式/顯式類型轉換 | C++四種強制類型轉換、類的隱式轉換、explicit

文章目錄

  • C語言類型轉換
    • 隱式類型轉換
    • 顯式類型轉換
  • C++ 強制類型轉換
    • static_cast
    • reinterpret_cast
    • const_cast
    • dynamic_cast
  • 類的隱式類型轉換
    • 概念
    • 只允許一步類類型轉換
    • explicit 抑制構造函數定義地隱式轉換
    • 可以通過顯式轉換使用explicit構造函數


C語言類型轉換

隱式類型轉換

編譯器在編譯階段自動進行,通常適用于相近的類型,如果不能轉換則會編譯失敗。

何時發生隱式類型轉換:

  • 在大多數表達式中,比int類型小的整數值提升為較大的整數類型
  • 在條件中,非布爾值轉換成布爾類型
  • 初始化過程中,初始值轉換成變量的類型;在賦值語句中,右側運算對象轉化成左側運算對象的類型
  • 算術運算或關系運算對象有多種類型,需要轉換成同一種類型
  • 函數調用時也會發生類型轉換

顯式類型轉換

需要用戶自己處理,通常用于不相近類型的轉換。

int main()
{int i = 9;int* p = &i;//將指針轉換為地址int addr = (int)p;cout << addr;
}

C語言的類型轉換使用起來很簡單,但是也有很大的缺點:

  • 隱式類型轉換 可能會因為整形提升或者數據截斷導致 精度的丟失,并且有時候會因為 忽略隱式類型轉換導致錯誤發生
  • 顯示類型轉換 代碼不夠清晰,沒有很好的將各種情況劃分開,而是全部混在一起使用。

C++ 強制類型轉換

C++為了加強類型轉換的可視性,引入了四種命名的強制類型轉換操作符:static_cast、reinterpret_cast、const_cast、dynamic_cast

static_cast

static_cast 用于非多態類型的轉換(靜態轉換),編譯器隱式執行的任何類型轉換都可用 static_cast ,但它不能用于兩個不相關的類型進行轉換。(即對應C語言中的隱式類型轉換)

int main()
{double d = 1.9;int i = static_cast<int>(d);cout << i;
}

reinterpret_cast

reinterpret_cast 是一種較為危險的類型轉換,通常為操作數的位模式提供較低層次的重新解釋,用于 將一種類型轉換為另一種不同的類型 ,通常適用于指針、引用、以及整數之間的類型轉換。

int main()
{int i = 9;int* p = &i;double* p2 = reinterpret_cast<double*>(p);cout << *p2 << ' ' << *p;
}

在這里插入圖片描述
如上面所說,這種轉換十分容易導致錯誤的發生,因為指針類型其實是其指向的地址的類型,決定了指針看待這段地址的方式,它該如何讀取數據。這里我把 int 改成了 double,使得他原本應該讀取 4個字節,而變成了 8個字節,就導致了數據的變化。如果使用不當很容易會造成越界訪問導致程序崩潰。

這篇文章也很有意思。


const_cast

const_cast 通常用于刪除變量的const屬性。

如果想要修改 const變量的值,就需要用 volatile 來取消編譯器優化。因為 const變量 創建后會被放入寄存器中,只有我們取 const變量 的地址時,他才會在內存中申請空間。通常我們想要修改的是 const變量在內存中的值 ,但是由于 編譯器優化,當使用 const變量 時就會到 寄存器 中去取值,所以需要用 volatile 取消編譯器優化,讓其每次在內存中取值。

不加 volatile 時:

int main()
{const int ci = 10;int* pi = const_cast<int*>(&ci); // 對應c語言強制類型轉換中去掉const屬性的(不相近類型)*pi = 20;cout << ci << ' ' << *pi;
}

在這里插入圖片描述
可以看出 ciconst屬性 仍在,其值并未更改,只改了 *pi 的值。

volatile 時:

int main()
{volatile const int ci = 10;int* pi = const_cast<int*>(&ci); // 對應c語言強制類型轉換中去掉const屬性的(不相近類型)*pi = 20;cout << ci << ' ' <<  *pi;
}

在這里插入圖片描述


dynamic_cast

dynamic_cast 是一種動態的類型轉換,是C++新增的概念,用于將一個 父類對象的指針/引用轉換為子類對象的指針/引用

派生類 可以賦值給 基類 的對象、指針或者引用,這樣的賦值也叫做對象切割。

例如Human類派生出的Student類:
在這里插入圖片描述
當把 子類 賦值給 父類 時,可以通過切割掉多出來的成員 _stuNum 的方式來完成賦值。

但是 基類 對象如果想賦值給 派生類 ,則不可以,因為他不能憑空多一個 _stuNum 成員出來。

但是基類的 指針或者引用 卻可以強制類型轉換賦值給派生類對象。

  • 這個過程有可能成功,也有可能會因為越界導致出現問題。 如果使用C語言的強制類型轉換,很可能就會出現問題,因為其沒有安全保障。
  • 而如果使用 dynamic_cast ,則能夠保證安全,因為其會先檢查轉換是否能夠成功,如果不能成功則返回 0,能則直接轉換。但是 dynamic_cast 的向下轉換只支持繼承中的 多態 類型,也就是 父類之中必須包含虛函數
int main()
{Human h1;Student s1;Human* hPtrs = &s1; // 指向派生類對象Human* hPtrh = &h1; // 指向基類對象//傳統方法Student* pPtr = (Student*)hPtrs; // 沒問題Student* pPtr = (Student*)hPtrh; // 有時候沒有問題,但是會存在越界風險//dynamic_castStudent* pPtr = dynamic_cast<Student*>(hPtrh);return 0;
}

dynamic_cast 是如何識別父類的指針指向的是父類對象還是子類對象的呢?

其原理就是在運行時通過查找虛函數表上面的標識信息,來確認其指向的到底是父類還是子類,這也就是為什么只能用于含有虛函數的類。

這種在 運行中進行類型識別的方法 ,也叫做 RTTI ,C++中有很多支持 RTTI 的方法,如 dynamic_casttypeiddecltype


類的隱式類型轉換

概念

類的隱式類型轉換: 如果類有需要 一個實參 的構造函數(或者構造函數雖然有 N 個參數,但這些參數都有缺省值),那么可以使用 該實參類型 構造一個 具有常量屬性的臨時類。這樣的行為看起來像從 實參類型類類型 的隱式轉換。

示例如下:

class People{int age;string name;
public:People(int a){age = a;name = "zhangsan";}People(string b){age = 10;name = b;}People(){age = 21;name = "lihua";}People& add(const People &temp){age += temp.age;return *this;}People& revise(const People &temp){name = temp.name;return *this;}int getage(){return this->age;}string getname(){return this->name;}
};
int main()
{int num = 30;string who = "zhaoliu";People p1,p2(12);p1.add(num);cout << p1.getage() << endl;p2.revise(who);cout << p2.getname() << endl;
}

輸出結果:
在這里插入圖片描述
People 類中,接收 intstring 的構造函數分別運用了People類類型的隱式轉換機制(即分別定義了從 intstring 類型向 People 隱式轉化的規則)。換言之,在需要People的地方,我們可以使用string或者int作為替代

在上述代碼main函數中:

int num = 30;
p1.add(num);
// 用num構造一個臨時的People對象,該對象的name為空串,age等于30

我們用int實參充當了add的成員。該操作是合法的,編譯器用給定的int自動創建了一個People對象。新生成的這個臨時People對象被傳遞給add。因為add的參數是一個常量引用,所以我們可以給該參數傳遞一個臨時量。(因為臨時量具有常量屬性)

那如果add的參數不是常量引用,以上操作還可以進行嗎?

答案是否定的,因為num構造臨時的people經過了形如下式的轉化:

const People temp = People(num);

之后是將temp臨時量作為實參傳入add函數

p1.add(temp)

而如若add的形參只是一個普通引用的話,是無法綁定具有常量屬性的臨時量temp的。


只允許一步類類型轉換

編譯器只會自動地執行一步類型轉換。

下面的調用是錯誤的:

p2.revise("huangba");
// error:隱式地使用了兩種轉換規則
// (1)把"huangba"轉換成string
// (2)再把臨時的string轉換成People

如果想完成上述調用,可以顯式地將字符串轉換為string或者People對象:

  // 顯式地轉換成string,隱式地轉化成Peoplep2.revise(string("huangba"));// 顯式地轉換成People,隱式地轉化成stringp2.revise(People("huangba"));

explicit 抑制構造函數定義地隱式轉換

我們之前提到,如果構造函數有多個參數,但這些參數都有默認實參,那么同樣遵循類的隱式轉換規則:

class Date
{
public:Date(int year, int month = 4, int day = 24):_year(year),_month(month),_day(day){}int _year;int _month;int _day;
};int main()
{Date d1(2020, 4, 24);Date d2 = 2020;//C++98Date d3 = { 2020, 5 }; //C++11Date d4 = { 2020, 5, 26 }; //C+11
}

這里其實是先用這個整型值來調用了缺省的構造函數來創建了一個臨時對象,再使用這個對象來為 d2、d3、d4 賦值

如果想要禁止隱式轉換,可以通過將構造函數聲明為 explicit 加以阻止:

在這里插入圖片描述
在這里插入圖片描述
此時,沒有任何構造函數能用于隱式地創建 Date 對象,隱式轉換就不會發生了。

  1. 關鍵字 explicit 只對需要一個實參的構造函數有效,需要多個實參的構造函數不能用于執行隱式轉換,所以無須將這些構造函數指定為 explicit
  2. 只能在類內聲明構造函數時使用explicit關鍵字,在類外部定義時不應重復。
  3. explicit 構造函數只能用于 直接初始化 ,而且,編譯器不會在自動轉換過程中使用該構造函數。如下:
    在這里插入圖片描述

可以通過顯式轉換使用explicit構造函數

編譯器不會將具有 explicit 屬性的構造函數用于隱式轉換過程,但是我們可以顯式地進行轉換(強制類型轉換):
在這里插入圖片描述

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

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

相關文章

string對象和C風格字符串

混用string對象和C風格字符串 我們都知道允許使用字符串字面值來初始化string對象&#xff1a; string s("Hello World!");C規定&#xff0c;任何出現字符串字面值的地方都可以用以空字符結束的字符數組來替代&#xff1a; 允許使用以空字符結束的字符數組來初始化…

函數重載、引用再探、內聯函數

文章目錄函數重載為什么C支持重載&#xff0c;C語言不支持呢&#xff1f;extern “C”引用再探引用的特性引用的使用場景引用和指針引用和指針的不同點:內聯函數什么是內聯函數&#xff1f;內聯函數的特性內聯函數的好處類的內聯成員函數的聲明內聯函數的使用constexpr函數概念…

類的概念、成員函數的定義方式、類的訪問控制和封裝、類的大小、this指針

文章目錄類的概念structclassclass和struct的區別是什么呢&#xff1f;類中成員函數的兩種定義方式聲明和定義都在類中聲明和定義分離類的訪問控制和封裝類的封裝特性類的大小結構體內存對齊規則類的存儲方式this指針類的概念 在C中&#xff0c;類可以說是最重要的東西&#x…

jQuery實現兩個列表框的值之間的互換:

jQuery實現兩個列表框的值之間的互換&#xff1a; 截圖如下&#xff1a; 代碼如下&#xff1a; <% page language"java" import"java.util.*" pageEncoding"UTF-8"%> <%String path request.getContextPath();String basePath reque…

類的6個默認成員函數:構造函數、析構函數、拷貝構造函數、重載運算符、三/五法則

文章目錄6個默認成員函數構造函數概念默認構造函數的類型默認實參概念默認實參的使用默認實參聲明全局變量作為默認實參某些類不能依賴于編譯器合成的默認構造函數第一個原因第二個原因第三個原因構造函數初始化構造函數里面的“”是初始化嗎&#xff1f;為什么要使用列表初始化…

C++ 類的知識 | 構造函數再探、匿名對象、友元函數、內部類、類的const成員、類的static成員

文章目錄構造函數再探以下代碼共調用多少次拷貝構造函數委托構造函數概念形式匿名對象友元友元的聲明友元類令成員函數作為友元函數重載和友元注意內部類特性類的const成員可變數據成員類的static成員概念關于static靜態成員的類內初始化靜態成員能用于某些普通成員不能的場景構…

截取全部數值字符并將其轉化為數值類型

功能 從name中找出全部數值字符&#xff0c;之后將name&#xff08;string類&#xff09;轉為d&#xff08;double類&#xff09; 代碼 #include <iostream> #include <list> #include <deque> #include <vector> #include <forward_list> #i…

替換string中的部分字符

功能 向函數fun中傳入三個參數&#xff1a;將s中所有oldval替換為newval 代碼 #include <iostream> #include <list> #include <deque> #include <vector> #include <forward_list> #include <array> using namespace std;void fun(str…

順序容器(vector、list、string、deque、forward_list)及迭代器、容器適配器

文章目錄概述所有容器都支持的操作迭代器迭代器支持的操作迭代器支持的算術運算容器類型size_typeiterator 和 const_iterator容器定義和初始化拷貝初始化順序容器獨有的構造函數&#xff08;array除外&#xff09;array的初始化與內置數組類型的區別6種初始化方法&#xff08;…

jQuery實現表格隔行換顏色:

jQuery實現表格各行換顏色&#xff1a; 截圖如下&#xff1a; 代碼如下&#xff1a; <span style"font-family:Microsoft YaHei;font-size:14px;"><% page language"java" import"java.util.*" pageEncoding"UTF-8"%> &…

用stack處理中綴表達式【+、-、*、/、()】

文章目錄題目描述思路使用getline()存儲輸入的字符串邊讀取邊壓棧完整代碼題目描述 使用stack處理括號化的表達式。當你看到一個左括號&#xff0c;將其記錄下來。當你在一個左括號之后看到一個右括號&#xff0c;從stack中pop對象&#xff0c;直至遇到左括號&#xff0c;將左括…

原地置換法尋找數組中重復的數

文章目錄題目描述代碼實現題目描述 在一個長度為 n 的數組 nums 里的所有數字都在 0&#xff5e;n-1 的范圍內。數組中某些數字是重復的&#xff0c;但不知道有幾個數字重復了&#xff0c;也不知道每個數字重復了幾次。請找出數組中任意一個重復的數字。 輸入&#xff1a; [2,…

二維數組的查找

文章目錄題目描述思路注意代碼題目描述 在一個 n * m 的二維數組中&#xff0c;每一行都按照從左到右遞增的順序排序&#xff0c;每一列都按照從上到下遞增的順序排序。請完成一個高效的函數&#xff0c;輸入這樣的一個二維數組和一個整數&#xff0c;判斷數組中是否含有該整數…

雙指針

文章目錄題目描述思路注意代碼實現題目描述 請實現一個函數&#xff0c;把字符串 s 中的每個空格替換成"%20"。 示例 1&#xff1a; 輸入&#xff1a;s “We are happy.” 輸出&#xff1a;“We%20are%20happy.” 限制&#xff1a; 0 < s 的長度 < 10000 思…

Springmvc,Spring MVC文件上傳

Springmvc文件上傳&#xff1a; 1.代碼截圖如下&#xff1a; 2.UploadController.java: package cn.csdn.controller;import java.io.File;import javax.servlet.http.HttpServletRequest;import org.springframework.stereotype.Controller; import org.springframework.ui.…

倒序輸出鏈表

文章目錄題目描述思路遞歸法棧題目描述 輸入一個鏈表的頭節點&#xff0c;從尾到頭反過來返回每個節點的值&#xff08;用數組返回&#xff09;。 示例 1&#xff1a; 輸入&#xff1a;head [1,3,2] 輸出&#xff1a;[2,3,1] 限制&#xff1a; 0 < 鏈表長度 < 10000 思…

插入迭代器、流迭代器、反向迭代器、移動迭代器

文章目錄前言插入迭代器inserterfront_inserterback_inserteriostream迭代器istream_iterator 讀取輸入流istream_iterator允許使用懶惰求值ostream_iterator操作反向迭代器reverse_iterator的base成員函數前言 除了為每個容器定義的迭代器之外&#xff0c;標準庫在頭文件iter…

泛型算法(lambda表達式、function類模板、bind函數適配器、迭代器類別、鏈表數據結構獨有的算法)

文章目錄概念find()函數迭代器令算法不依賴于容器但算法依賴于元素類型的操作算法永遠不會執行容器的操作只讀算法accumulate()函數從兩個序列中讀取元素&#xff08;equal函數為例&#xff09;迭代器作為參數形成兩個序列equal()寫容器元素的算法概念fill()fill_n()插入迭代器…

jsp,div 限制字數,超出部分用省略號代替

1.我是用struts2標簽做的&#xff1a;如下&#xff1a; <% page language"java" import"java.util.*" pageEncoding"UTF-8"%> <% taglib prefix"s" uri"/struts-tags"%> <%String path request.getContext…

C++之關聯容器

文章目錄概述及類型mapsetpair類型概念初始化默認初始化提供初始化器允許的操作可以創建一個pair類的函數可以作為容器的類型關聯容器迭代器概念map的迭代器set的迭代器是const的初始化map and setmultimap and multiset關聯容器的操作額外的類型別名關聯容器和算法刪除元素添加…