c++ 四种类型转换机制
類型轉換機制可以分為:隱式類型轉換 和 顯示類型轉換(強制類型轉換)
C中的類型轉換:
事情要從頭說起,這個頭就是C語言.我們已經習慣了使用C-like類型轉換,因為它強大而且簡單.
主要有一下兩種形式:
?
- (new-type) expression
- new-type (expression)
?
C艸中的類型轉換:
隱式類型轉換比較常見,在混合類型表達式中經常發生.比如在表達式中存在short和int,那么就過會發生整型提升.四種強制類型轉換操作符:static_cast、dynamic_cast、const_cast、reinterpret_cast。
1. static_cast與dynamic_cast:
把這兩個放在一起比較容易記憶,"一靜一動".從字面上也可以看出,前者提供的是編譯時期的靜態類型檢測,后者提供的是
運行時檢測.
- static_cast: 1)完成基礎數據類型,2)同一個繼承體系中類型的轉換 3)任意類型與空指針類型void*之間的轉換。
- dynamic_cast:使用多態的場景,增加了一層對真實調用對象類型的檢查
?
在上面的例子中,Clike可以運行,然而修改過的*p如果使用,運行結果將會出現錯誤,而使用static_cast可以將錯誤在編譯時期檢查出,.
在不同繼承體系的自定義類型中:
?
?
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; }; A *ptrA = new A();C *ptrC = (C *)(ptrA);ptrC->printC(); //"call printC() in class C"//ptrC = static_cast<C*>(ptrA); //編譯報錯:error: invalid static_cast from type 'A*’ to type C*’?
上面A C是兩個無關的類,然而使用Clike可以實現這種類型的強制轉換,這是十分危險的! 使用static_cast可以將這種潛在的危險在編譯器找出來.
在同一繼承體系中:
upcast(向上轉換即子類轉成父類):沒有問題.因為父類的行為都包含在子類中;
downcast(向下轉換):有可能會出現問題,編譯時可能不會發現.
一個類的行為和自身的類型相關.也就是一個A類型的指針總會優先調用自己A類內的函數,當然發生繼承中的重寫(虛繼承等)例外.
#include <iostream> #include <cstdio>using namespace std; 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();A *ptrA = static_cast<B *>(ptrB);ptrA -> printA();ptrA -> printSum();//打印結果:sum = 2//在進行upcast的時候,指針指向的對象的行為與指針的類型相關。 ptrA = new A;ptrB = static_cast<B *>(ptrA);ptrB -> printB();ptrB -> printSum();//打印結果:sum = 0//在進行downcast的時候,其行為是“undefined”。 B b;B &rB = b;rB.printSum();//打印結果: sum = 4A &rA = static_cast<A &>(b);rA.printA();rA.printSum();//打印結果: sum = 2//在進行upcast的時候,指針指向的對象的行為與引用類型相關. A a;A &rA1 = a;rA.printSum();B &rB1 = static_cast<B &>(a);rB1.printB();//打印結果:sum = 4 rB1.printSum();//打印結果 :sum = 1.45863e-316//在進行downcast的時候,其行為是“undefined”。return 0; }
這里其實很明顯,在downcast轉換的時候,會出現一些跟指針或者引用類型相關的函數調用,但是因為指針或者引用(父類)
沒有定義這些行為,因為調用到了這些行為導致出現了未定義的行為.
明顯解決這個問題的辦法就是,虛函數!如果聲明A類中的printSum未 虛函數,那么子類B就會有一個虛表,虛表中的第一個函數就是printSum函數其實是
B類的該函數.所以,A類指針調用該函數就會調用B類中的該函數 顯示結果sum= 4. 在未定義之前sum = 2(A類中的該函數).
PS:引用類型必須被初始化,這是引用和指針類型的重要區別.
總之,就是盡可能不要使用downcast也就是 使用子類的指針指向父類.
感覺這里又不得不說,c++內存對象的對齊方式.所以 ,在另外一篇blog<c++內存的對齊方式>中理清楚這些問題.
?
?
?
?
?
dynamic_cast
1.dynamic_cast是在運行時檢查的,用于在集成體系中進行安全的向下轉換downcast(當然也可以向上轉換,但沒必要,因為可以用虛函數實現)
? ?即:基類指針/引用 -> 派生類指針/引用
? ?如果源和目標沒有繼承/被繼承關系,編譯器會報錯!
2.dynamic_cast是4個轉換中唯一的RTTI操作符,提供運行時類型檢查。
3.dynamic_cast不是強制轉換,而是帶有某種”咨詢“性質的,如果不能轉換,返回NULL。這是強制轉換做不到的。
4.源類中必須要有虛函數,保證多態,才能使用dynamic_cast<source>(expression)
static_cast
用法:static_cast < type-id > ( expression )
該運算符把expression轉換為type-id類型,在編譯時使用類型信息執行轉換,在轉換執行必要的檢測(指針越界,類型檢查),其操作數相對是安全的。
但沒有運行時類型檢查來保證轉換的安全性。
reinterpret_cast
僅僅是復制n1的比特位到d_r, 沒有進行必要的分析.interpret_cast是為了映射到一個完全不同類型\
的意思,這個關鍵詞在我們需要把類型映射回原有類型時用到它。我們映射到的類型僅僅是為了故弄\
玄虛和其他目的,這是所有映射中最危險的。(這句話是C++編程思想中的原話
const_cast
去除const常量屬性,使其可以修改
and
volatile屬性的轉換? 易變類型<->不同類型.
Code:
#include <iostream> #include <cstdio> #include <string>using namespace std;class A {public:A(){};int m_a; };class B {public:int m_b; };class C : public A, public B {};int main() {const A a;//a.num = 1;const_cast<A&>(a).m_a = 2;//a.num = 3;編譯不能通過,說明const_cast只能轉換一次,不是永久脫離原有const屬性cout<<a.m_a<<endl;int n = 9;double d_s = static_cast<double>(n);double d_r = reinterpret_cast<double&>(n);cout<<d_r<<endl;//4.24399e-314//在進行計算以后, d_r包含無用值. 這是因為 reinterpret_cast\ 僅僅是復制n1的比特位到d_r, 沒有進行必要的分析.interpret_cast是為了映射到一個完全不同類型\的意思,這個關鍵詞在我們需要把類型映射回原有類型時用到它。我們映射到的類型僅僅是為了故弄\玄虛和其他目的,這是所有映射中最危險的。(這句話是C++編程思想中的原話C c;printf("%p, %p, %p\n", &c, reinterpret_cast<B*>(&c), static_cast <B*>(&c));//前兩個的輸出值是相同的,最后一個則會在原基礎上偏移4個字節,這是因為static_cast計算了父子類指針轉換的偏移量,\并將之轉換到正確的地址(c里面有m_a,m_b,轉換為B*指針后指到m_b處),而reinterpret_cast卻不會做這一層轉換\因此, 你需要謹慎使用 reinterpret_cast.return 0; }?
#include <iostream> #include <typeinfo> #include <cstdio>using namespace std;class A{ public:virtual void foo(){cout<<"A foo"<<endl;}//虛函數的出現會帶來動態機制 Class A 至少要有一個虛函數void pp(){cout<<"A pp"<<endl;} };class B: public A{ public:void foo(){cout<<"B foo"<<endl;}void pp(){cout<<"B PP"<<endl;}void functionB(){cout<<"Excute FunctionB!"<<endl;} };int main() {B b;A *pa = &b;pa->foo();pa->pp();//基類指針可以指向派生類,但是只能調用基類和派生類都存在的成員,也就是說不能調用派生類中新增的成員!//pa->FunctionB();//error: 'class A' has no member named 'FunctionB'if(dynamic_cast<B*>(pa) == NULL){cout<<"NULL"<<endl;}else{cout<<typeid((dynamic_cast<B*>(pa))).name()<<endl;dynamic_cast<B*>(pa)->foo();dynamic_cast<B*>(pa)->pp();dynamic_cast<B*>(pa)->functionB();}A aa;//B *pb = &aa;派生類不能指向基類B *pbNull = NULL;pbNull->functionB();//finepbNull->pp();//fine//pbNull->functionB(); crash!foo調用了虛函數,編譯器需要根據對象的虛函數指針查找虛函數表,但為空,crash!return 0; }?
轉載于:https://www.cnblogs.com/luntai/p/5879026.html
總結
以上是生活随笔為你收集整理的c++ 四种类型转换机制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: oracle+SQL优化实例
- 下一篇: 大道至简第一章 读后感