国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - C/C++ - C++中的四種類型轉換

C++中的四種類型轉換

2021-03-07 14:35C++教程網 C/C++

類型轉換有c風格的,當然還有c++風格的。c風格的轉換的格式很簡單(TYPE)EXPRESSION,但是c風格的類型轉換有不少的缺點,有的時候用c風格的轉換是不合適的,因為它可以在任意類型之間轉換,

1 引子

這篇筆記是根據StackOverflow上面的一個問題整理而成,主要內容是對C/C++當中四種類型轉換操作進行舉例說明。在之前其實對它們都是有所了解的,而隨著自己在進行總結,并敲了一些測試示例代碼進行驗證之后,對它們的理解又深刻了一些。

總所周知,在C++ 當中引入了四種新的類型轉換操作符:static_cast, dynamic_cast, reinterpret_cast,還有const_cast。就自己見過的一些C++代碼當中,它們的使用其實并不普遍。不少程序員依然樂于去使用C-like的類型轉換,因為它強大且編寫起來又簡單。據說C-Like類型轉換操作符的作用實際上已經包括了static_cast, const_cast和reinterpret_cast三種操作符,你相信嗎?一起來著看。

注:上面提到的C-Like類型轉換操作有如下的兩種形式,這一點大家一定都不會陌生。

?
1
2
(new-type) expression
new-type (expression)

2 static_cast vs dynamic_cast

之所以把static_cast與dynamic_cast兩兄弟放在一起是因為它們兩者對比起來更容易記得住。首先,從名稱上面它們就有語義相對的關系,一“靜”一“動”。另外,在功能上面也在一定程度上體現了這一對比的特性,如dynamic_cast的Run-time Checkingt,static_cast在編譯時增加的類型檢測。簡單而言:

static_cast: 1)完成基礎數據類型,2)同一個繼承體系中類型的轉換
dynamic_cast:使用多態的場景,增加了一層對真實調用對象類型的檢查

2.1 從C-Like到static_cast

static_cast對于基礎類型如int, float, char以及基礎類型對應指針的處理大多情況下恰如C-Like的轉換一樣,不過static_cast會來得更加安全。

?
1
2
3
4
5
char c = 10;      // 1 個字節
int *p = (int *)&c;  // 4 個字節(32bit platform)
 
*p = 5;        // 內存踩臟
int *q = static_cast<int *>(&c); // 使用static_cast可在編譯階段將該錯誤檢查出來。


對于自定義類型的處理,相比C-Like而言,它也多了一層保護,也就是它不支持在不屬于同一繼承體系的類型之間進行轉換。但是C-Like就可以辦到,看下面這個例子:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
 
class A
{
public:
 A(){}
 ~A(){}
 
private:
 int i, j;
};
 
class C
{
public:
 C(){}
 ~C(){}
 
 void printC()
 {
  std::cout <<"call printC() in class C" <<std::endl;
 }
private:
 char c1, c2;
};
 
int main()
{
 A *ptrA = new A;
 //C *ptrC = static_cast<C *>(ptrA);
 // 編譯無法通過,提示:
 // In function ‘int main()':
 // error: invalid static_cast from type ‘A*' to type ‘C*'
 
 C *ptrC = (C *)(ptrA);
 ptrC->printC();
 // 編譯正常通過。
 // 盡管這個時候能夠正常調用printC,但實際上這種做法的結果是“undefined”
 // 嘗試過,如果添加一些數據成員的運算,這個時候將會使得運算結果無法預測
 // 所以,在運行時候該邏輯相關的行為是不清晰的。
 
 return 0;
}

2.2 static_cast對于自定義類型的轉換

上面這個小例子簡單對比了static_cast與C-Like在針對不同繼承體系的類之間表現的差異性,現在先把范圍縮小到同一繼承體系當中的類型轉換。(注:這里所說的類型一般是針對類的指針或者類的引用)

static_cast針對同一繼承體系的類之間的轉換,它既可以進行upcast也可以進行downcast。一般來說,在進行upcast時是沒有問題的,畢竟子類當中一定包含有父類的相關操作集合,所以通過轉換之后的指針或者引用來操作對應的對象,其行為上是可以保證沒問題。這和使用static_cast與使用C-Like或者直接隱式轉換效果一樣(當然,其結果是否符合程序員本身的預期與當時的設計有關系)。

需要注意的是,使用static_cast進行downcast應該避免,因為它可以順利逃過編譯器的法眼,但在運行時卻會爆發未定義的問題:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <iostream>
 
class A
{
public:
 A():i(1), j(1){}
 ~A(){}
 
 void printA()
 {
  std::cout <<"call printA() in class A" <<std::endl;
 }
 
 void printSum()
 {
  std::cout <<"sum = " <<i+j <<std::endl;
 }
 
private:
 int i, j;
};
 
class B : public A
{
public:
 B():a(2), b(2) {}
 ~B(){}
 
 void printB()
 {
  std::cout <<"call printB() in class B" <<std::endl;
 }
 
 void printSum()
 {
  std::cout <<"sum = " <<a+b <<std::endl;
 }
 
 void Add()
 {
  a++;
  b++;
 }
 
private:
 double a, b;
};
 
int main()
{  
 B *ptrB = new B;
 ptrB->printSum();
 //打印結果:sum = 4
 A *ptrA = static_cast<B *>(ptrB); 
 ptrA->printA();
 ptrA->printSum();
 //打印結果:sum = 2
 //在進行upcast的時候,指針指向的對象的行為與指針的類型相關。
 
 
 ptrA = new A;
 ptrA->printSum();
 //打印結果:sum = 2
 ptrB = static_cast<B *>(ptrA);
 ptrB->printB();
 ptrB->printSum();
 //打印結果:sum = 0
 //在進行downcast的時候,其行為是“undefined”。
 
 //B b;
 //B &rB = b;
 //rB.printSum();
 //打印結果:sum = 4
 //A &rA = static_cast<A &>(rB); 
 //rA.printA();
 //rA.printSum();
 //打印結果:sum = 2
 //在進行upcast的時候,引用指向的對象的行為與引用的類型相關。
 
 //A a;
 //A &rA = a;
 //rA.printSum();
 //打印結果:sum = 4
 //B &rB = static_cast<B &>(rA); 
 //rB.printB();
 //rB.printSum();
 //打印結果:sum = 5.18629e-317
 //在進行downcast的時候,其行為是“undefined”。
 
 return 0;
}

如上,static_cast在對同一繼承體系的類之間進行downcast時的表現,與C-Like針對分屬不同繼承體系的類之間進行轉換時的表現一樣,將是未定義的。所以,應該盡可能使用static_cast執行downcast轉換,更準確的說,應該盡可能避免對集成體系的類對應的指針或者引用進行downcast轉換。

既然這樣,那是不是在軟件開發過程當中就不會存在downcast的這種情況了呢?實際上不是的。一般來說,進行downcast的時候一般是在虛繼承的場景當中,這個時候dynamic_cast就上場了。

2.3 dynamic_cast

dynamic_cast的使用主要在downcast的場景,它的使用需要滿足兩個條件:

downcast時轉換的類之間存在著“虛繼承”的關系
轉換之后的類型與其指向的實際類型要相符合
dynamic_cast對于upcast與static_cast的效果是一樣的,然而因為dynamic_cast依賴于RTTI,所以在性能上面相比static_cast略低。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <iostream>
#include <exception>
 
class A
{
public:
 virtual void print()
 {
  std::cout <<"Welcome to WorldA!" <<std::endl;
 }
};
 
class B : public A
{
public:
 B():a(0), b(0) {}
 ~B(){}
 virtual void print()
 {
  std::cout <<"Welcome to WorldB!" <<std::endl;
 }
private:
 double a, b;
};
 
int main()
{
 B *ptrB = new B;
 A *ptrA = dynamic_cast<A *>(ptrB);
 ptrA->print();
 //在虛繼承當中,針對指針執行upcast時dynamic_cast轉換的效果與static_cast一樣
 //對是否存在virtual沒有要求,會實際調用所指向對象的成員。
  
 //A *ptrA = new A;
 //B *ptrB = dynamic_cast<B *>(ptrA);
 //ptrB->print();
 //Segmentation fault,針對指針執行downcast時轉換不成功,返回NULL。
 
 //A a;
 //A &ra = a;
 //B &b = dynamic_cast<B &>(ra);
 //b.print(); 
 //拋出St8bad_cast異常,針對引用執行downcast時轉換不成功,拋出異常。
 
 //ptrA = new A;
 //ptrB = static_cast<B *>(ptrA);
 //ptrB->print();
 //使用static_cast進行downcast的時候,與dynamic_cast返回NULL不同,
 //這里會調用ptrB實際指向的對象的虛函數。
  
 //ptrA = new A;
 //ptrB = dynamic_cast<B *>(ptrA);
 //ptrB->print();
 //在進行downcast時,如果沒有virtual成員,那么在編譯時會提示:
 // In function ‘int main()':
 // cannot dynamic_cast ‘ptrA' (of type ‘class A*') to type ‘class B*' (source type is not polymorphic)
 
 return 0;
}

從這個例子可以看出,在虛繼承場景下,能夠使用dynamic_cast的地方一定可以使用static_cast,然而dynamic_cast卻有著更嚴格的要求,以便幫助程序員編寫出更加嚴謹的代碼。只不過,它在性能上面多了一部分開銷。

3 reinterpret_cast

reinterpret_cast是最危險的一種cast,之所以說它最危險,是因為它的表現和C-Like一般強大,稍微不注意就會出現錯誤。它一般在一些low-level的轉換或者位操作當中運用。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <iostream>
 
class A
{
public:
 A(){}
 ~A(){}
 void print()
 {
  std::cout <<"Hello World!" <<std::endl;
 }
};
 
class B
{
public:
 B():a(0), b(0) {}
 ~B(){}
 
 void call()
 {
  std::cout <<"Happy for your call!" <<std::endl;
 }
 
private:
 double a, b;
};
 
int main()
{
 //A *ptrA = new A;
 //B *ptrB = reinterpret_cast<B *>(ptrA);
 //ptrB->call();
 //正常編譯
 //A *ptrA = new A;
 //B *ptrB = (B *)(ptrA);
 //ptrB->call();
 //正常編譯
 //A *ptrA = new A;
 //B *ptrB = static_cast<B *>(ptrA);
 //ptrB->call();
 //編譯不通過,提示:
 //In function ‘int main()':
 //error: invalid static_cast from type ‘A*' to type ‘B*'
 
 //char c;
 //char *pC = &c;
 //int *pInt = static_cast<int *>(pC);
 //編譯提示錯誤:error: invalid static_cast from type ‘char*' to type ‘int*'
 //int *pInt = reinterpret_cast<int *>(pC);
 //正常編譯。
 //int *pInt = (int *)(pC);
 //正常編譯。
 
 return 0;
}

分析了static_cast,dynamic_cast與reinterpret_cast之后就可以畫出如下的圖示對它們之間的區別進行簡單比較了。這里沒有將const_cast納入進來是因為它比較特殊,另外分節對它進行介紹。

?
1
2
3
4
5
6
7
8
9
    ----------------
    dynamic_cast \ -->同一繼承體系(virtual)的類指針或引用[更安全的downcast]
   ~~~~~~~~~~~~~~~~~~~~ 
   /   static_cast  \ -->基礎類型[更安全],同一繼承體系的類指針或引用
  ~~~~~~~~~~~~~~~~~~~~~~~~
  reinterpret_cast  \ -->與C-Like的作用一致,沒有任何靜態或者動態的checking機制
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 /     C-Like      \ -->基礎類型,同一繼承體系的類指針或引用,不同繼承體系類的指針或引用
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

4 const_cast

const_cast能夠使用來移出或者增加一個變量的const屬性,最初的時候我覺得這個const_cast比較怪異,C里面一直都沒有類似的東西來消除const屬性,這里是否會多余呢?其實,我這種想法本身就沒根沒據。后來想想,在C++當中一直提倡將常量聲明為const,這樣一旦常量變得多了起來,在與其他軟件組件或者第三方庫進行銜接的時候就難免會碰到需要cast const屬性的問題。比如:

?
1
2
3
4
5
6
7
8
9
10
const int myConst = 15;
int *nonConst = const_cast<int *>(&myConst);
 
void print(int *p)
{
  std::cout << *p;
}
 
print(&myConst); // 編譯錯誤:error: invalid conversion from ‘const int*' to ‘int*'
print(nonConst); // 正常

不過,在使用const_cast的時候應該要注意,如果沒有必要盡量不要去修改它的值:

?
1
2
3
4
5
const int myConst = 15;
int *nonConst = const_cast<int *>(&myConst);
 
*nonConst = 10;
// 如果該變量存放在read-only內存區當中,在運行時可能會出現錯誤。

5 小結

在C++當中對于大部分數據類型而言,使用C-Like的類型轉換已經完全夠用了。然而,不少人一直在倡導進行顯式數據類型轉換的時候盡可能地使用C++規定的類型轉換操作。我想這里面大概有兩方面的原因:

第一種,C++是一門“新”的編程語言,應該學會用它本身的思想來解決編程方面的問題;
第二種,盡管C-Like轉換操作能力強大,但如果將其任意使用,會產生不少在編譯期間隱藏,卻在運行時候神出鬼沒。這些問題使得軟件的行為極不清晰。
如此,C++當中引出了其他四種類型轉換方式,用來更加安全的完成一些場合的類型轉換操作。比如使用reinterpret_cast的時候會表示你確定無疑的想使用C-Like的類型轉換;在使用static_cast的時候想要確保轉換的對象基本兼容,比如無法將char *轉換為int *,無法在不同繼承體系類的指針或引用之間進行轉換;而使用dynamic_cast的時候是要對虛繼承下的類執行downcast轉換,并且已經明了當前性能已經不是主要的影響因素......

回答一下前文提到的問題??梢赃@么說,對于const_cast, static_cast, reinterpret_cast和dynamic_cast所能夠完成的所有轉換,C-Like也可以完成。但是,C-Like轉換卻沒有static_cast, dynamic_cast分別提供的編譯時類型檢測和運行時類型檢測。

C++之父Bjarne Stroustrup博士在這里也談到了他的觀點,主要有兩點:其一,C-Like的cast極具破壞性并且在代碼文本上也難得花不少力氣搜索到它;其二,新式的cast使得程序員更有目的使用它們并且讓編譯器能夠發現更多的錯誤;其三,新的cast符合模板聲明規范,可以讓程序員編寫它們自己的cast。

延伸 · 閱讀

精彩推薦
  • C/C++詳解c語言中的 strcpy和strncpy字符串函數使用

    詳解c語言中的 strcpy和strncpy字符串函數使用

    strcpy 和strcnpy函數是字符串復制函數。接下來通過本文給大家介紹c語言中的strcpy和strncpy字符串函數使用,感興趣的朋友跟隨小編要求看看吧...

    spring-go5642021-07-02
  • C/C++C++之重載 重定義與重寫用法詳解

    C++之重載 重定義與重寫用法詳解

    這篇文章主要介紹了C++之重載 重定義與重寫用法詳解,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內容,需要的朋友可以參考下...

    青山的青6062022-01-04
  • C/C++深入理解goto語句的替代實現方式分析

    深入理解goto語句的替代實現方式分析

    本篇文章是對goto語句的替代實現方式進行了詳細的分析介紹,需要的朋友參考下...

    C語言教程網7342020-12-03
  • C/C++C語言實現電腦關機程序

    C語言實現電腦關機程序

    這篇文章主要為大家詳細介紹了C語言實現電腦關機程序,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    xiaocaidayong8482021-08-20
  • C/C++C/C++經典實例之模擬計算器示例代碼

    C/C++經典實例之模擬計算器示例代碼

    最近在看到的一個需求,本以為比較簡單,但花了不少時間,所以下面這篇文章主要給大家介紹了關于C/C++經典實例之模擬計算器的相關資料,文中通過示...

    jia150610152021-06-07
  • C/C++c++ 單線程實現同時監聽多個端口

    c++ 單線程實現同時監聽多個端口

    這篇文章主要介紹了c++ 單線程實現同時監聽多個端口的方法,幫助大家更好的理解和學習使用c++,感興趣的朋友可以了解下...

    源之緣11542021-10-27
  • C/C++學習C++編程的必備軟件

    學習C++編程的必備軟件

    本文給大家分享的是作者在學習使用C++進行編程的時候所用到的一些常用的軟件,這里推薦給大家...

    謝恩銘10102021-05-08
  • C/C++C語言中炫酷的文件操作實例詳解

    C語言中炫酷的文件操作實例詳解

    內存中的數據都是暫時的,當程序結束時,它們都將丟失,為了永久性的保存大量的數據,C語言提供了對文件的操作,這篇文章主要給大家介紹了關于C語言中文件...

    針眼_6702022-01-24
主站蜘蛛池模板: 精品黄色大片 | 欧美在线视频网站 | 一区二区在线不卡 | 欧美日韩亚洲综合 | 日本不卡免费新一二三区 | yw193com尤物| 日韩看片| 色婷婷综合网 | 欧美一区二区三区 | 日韩成人在线一区 | 亚洲一一在线 | 91精品国产综合久久久久久 | 精品在线二区 | 中国一级毛片 | 国产乱码一区二区三区在线观看 | 99精品视频在线观看 | 免费一区在线观看 | 亚洲精品乱码久久久久久金桔影视 | 午夜电影网站 | 中文字幕一二三区 | 一区二区三区四区av | 国产成人久久av免费高清密臂 | 国产精品久久久久久久久久东京 | 尤物网址| 国产精品免费视频一区 | 玖玖爱国产 | 亚洲精品一区二区 | 午夜精品视频 | 欧洲一区 | 国产精品久久久久久久久久免费动 | 中文字幕亚洲精品 | 视频一区在线播放 | 久久精品视频免费 | 日本久久网 | 亚州中文字幕蜜桃视频 | 成人午夜在线视频 | 成人精品一区二区 | 日韩欧美在 | 欧美二区三区 | 中文字幕一区二区三区不卡 | 91精品国产91久久综合 |