類型轉(zhuǎn)換
- 隱式類型轉(zhuǎn)換
- 顯式類型轉(zhuǎn)換
語法
xxx_cast <類型> (表達式)
1. static_cast
- 用法
用于非多態(tài)類型之間的轉(zhuǎn)換,不提供運行時的檢查來確保轉(zhuǎn)換的安全性。
主要有如下
- 基本數(shù)據(jù)類型轉(zhuǎn)換
-
int
轉(zhuǎn)換成enum
- 基類和子類之間指針和引用的轉(zhuǎn)換
- 上行轉(zhuǎn)換掸刊,把子類的指針或引用轉(zhuǎn)換成父類,這種轉(zhuǎn)換是安全的(通常使用默認轉(zhuǎn)換)泞歉。
- 下行轉(zhuǎn)換锰提,把父類的指針或引用轉(zhuǎn)換成子類,這種轉(zhuǎn)換是不安全的阳准,也需要程序員來保證(通常使用
dynamic_cast
)氛堕。
1.1 基本數(shù)據(jù)類型轉(zhuǎn)換
-
int
轉(zhuǎn)換成char
int n = 97;
cout << n << '\t' << (char)n << '\t' << static_cast<char>(n) << endl;
-
int
轉(zhuǎn)換成float
int n = 1;
cout << n/2 << '\t' << (float)n/2 << '\t' << static_cast<float>(n)/2 << endl;
1.2 int
轉(zhuǎn)換成enum
enum Week{
SUN,MON,TUE,WED,THU,FRI,SAT
};
Week day = 0;
編譯上述代碼出現(xiàn)如下錯誤:
- g++編譯錯誤:
error: invalid conversion from ‘int’ to ‘Week’
- clang++編譯錯誤:
error: cannot initialize a variable of type 'const Week' with an rvalue of type 'int'
把代碼Week day = 0;
改為Week day = static_cast<Week>(0);
可以消除上面的錯誤。
1.3 基類和子類之間指針和引用的轉(zhuǎn)換
已知存在繼承關系的兩個類Base
與Derive
野蝇。
class Base{
public:
void Print(){cout << "Base" << endl;}
};
class Derive:public Base{
public:
void Print(){cout << "Derive" << endl;}
};
1.3.1 上行轉(zhuǎn)換
// 對象
Derive d;
Base b = static_cast<Base>(d);
b.Print();
// 引用
Base& fb = static_cast<Base&>(d);
fb.Print();
// 指針
Base* pb = static_cast<Base*>(new Derive);
pb->Print();
通常使用隱式轉(zhuǎn)換
// 對象
Derive d;
Base b = d;
b.Print();
// 引用
Base& fb = d;
fb.Print();
// 指針
Base* pb = new Derive;
pb->Print();
1.3.2 下行轉(zhuǎn)換
這種轉(zhuǎn)換不安全讼稚,通常使用dynamic_cast
括儒。
1.4 指針/引用轉(zhuǎn)換
void
指針轉(zhuǎn)換成目標類型的指針,這種轉(zhuǎn)換是不安全的锐想,也需要程序員來保證帮寻;
如下代碼編譯出錯,因為不能把void*
轉(zhuǎn)換成其他具體指針赠摇。
void* pd = new Derive;
Base* pb = pd; // error: invalid conversion from ‘void*’ to ‘Base*’
pb->Print();
把Base* pb = pd;
改為Base* pb = static_cast<Base*>(pd);
固逗,可以解決上述編譯錯誤。
static_cast
跟傳統(tǒng)小括號轉(zhuǎn)換方式幾乎是一致的藕帜。
問題:為什么要用static_cast
代替?zhèn)鹘y(tǒng)小括號轉(zhuǎn)換方式烫罩?
(來自《C++ prime 4th》5.12.7)
標準 C++ 為了加強類型轉(zhuǎn)換的可視性,引入命名的強制轉(zhuǎn)換操作符洽故,為程序員在必須使用強制轉(zhuǎn)換時提供了更好的工具贝攒。例如, 非指針的 static_cast 和const_cast 要比 reinterpret_cast 更安全时甚。結(jié)果使程序員(以及讀者和操縱程序的工具)可清楚地辨別代碼中每個顯式的強制轉(zhuǎn)換潛在的風險級別隘弊。
雖然標準 C++ 仍然支持舊式強制轉(zhuǎn)換符號,但是我們建議荒适,只有在 C 語言或標準 C++ 之前的編譯器上編寫代碼時梨熙,才使用這種語法。
2. const_cast
常量賦值給非常量時吻贿,會出現(xiàn)下面編譯錯誤。
const int a = 10;
// 指針
const int* cp = &a;
int* p = cp;// error: invalid conversion from ‘const int*’ to ‘int*’
// 引用
const int& cf = a;
int& f = cf;// error: binding ‘const int’ to reference of type ‘int&’ discards qualifiers
const_cast
主要作用是移除類型的const
屬性哑子。
- 常量指針被轉(zhuǎn)化成非常量的指針舅列,并且仍然指向原來的對象;
- 常量引用被轉(zhuǎn)換成非常量的引用卧蜓,并且仍然指向原來的對象帐要;
-
const_cast
一般用于修改底指針。如const char *p
形式弥奸。
const int a = 10;
// 指針
const int* cp = &a;
int* p = const_cast<int*>(cp);
// 引用
const int& cf = a;
int& f = const_cast<int&>(cf);
- 在類中的應用情景
1.const對象想使用類中非const成員函數(shù)
2.const成員函數(shù)中想修改某個成員變量
#include <iostream>
using namespace std;
//const成員函數(shù)修改const成員變量的兩種方法
//1.使用const_cast
//2.成員變量前加關鍵字mutable
class Test
{
int m_n;
int m_count;
//mutable允許成員變量在const成員函數(shù)中被改變
public:
Test(int n):m_n(n),m_count(0){}
void Add(int m)
{
m_n = m+m_n;
}
void Print()const
{
//在const函數(shù)中this指針是const榨惠,所以this指針下的成員都是const
cout<<m_n<<endl;
//cout<<"count= "<<++m_count<<endl;//這一句編譯會報錯,錯誤:increment of member ‘Test::m_count’ in read-only object
cout<<"count= "<<++const_cast<int&>(m_count)<<endl;//在const成員函數(shù)中修改輔助的成員變量
}
void PrintCount()const {cout<<m_count<<endl;}
};
int main()
{
//const_cast<>() :把const對象/指針/引用轉(zhuǎn)成非const對象/指針/引用
const Test t(10);
t.Print();
//t.Add(5);//這一句編譯會報錯盛霎,因為const對象只能調(diào)用const成員函數(shù)
//使用const_cast<>()將t轉(zhuǎn)為非const對象
const_cast<Test&>(t).Add(5);
t.Print();
t.PrintCount();
}
3. dynamic_cast
用于類的指針赠橙、類的引用或者void *
轉(zhuǎn)化。
主要用于以下三種情況:
- 上行轉(zhuǎn)換愤炸,把子類的指針或引用轉(zhuǎn)換成父類期揪,與
static_cast
相同。 - 下行轉(zhuǎn)換规个,把父類的指針或引用轉(zhuǎn)換成子類凤薛,比
static_cast
安全姓建。 - 交叉轉(zhuǎn)換,兄弟之間指針轉(zhuǎn)換缤苫,
static_cast
會出現(xiàn)編譯錯誤速兔。
如果時指針,進行正確的轉(zhuǎn)換活玲,獲得對應的值涣狗;否則返回NULL,如果是引用翼虫,則在運行時就會拋出異常屑柔;
-
dynamic_cast
使用條件
1.存在父子關系
2.父類中要存在多態(tài)
3.子類指針/引用指向父類指針/引用
下行轉(zhuǎn)換
#include <iostream>
using namespace std;
class Base {
public:
void Print() { cout << "Base" << endl; }
virtual ~Base(){}
};
class Derive : public Base {
public:
void Print() { cout << "Derive" << endl; }
};
int main() {
Base * pB = new Derive;
pB->Print();
Derive *pD = dynamic_cast<Derive*>(pB);
pD->Print();
}
交叉轉(zhuǎn)換
#include <iostream>
using namespace std;
class Base {
public:
void Print() { cout << "Base" << endl; }
virtual ~Base(){}
};
class Derive1 : public Base {
public:
void Print() { cout << "Derive1" << endl; }
};
class Derive2 : public Base {
public:
void Print() { cout << "Derive" << endl; }
};
int main() {
Derive1* pD1 = new Derive1;
pD1->Print();
Derive2 *pD2 = dynamic_cast<Derive2*>(pD1);
pD2->Print();
}
-
dynamic_cast
使用場景
1.如果我們想使用父類指針/引用調(diào)用子類特有的成員函數(shù)
2.可以通過返回值來判斷繼承關系(假設你定義了貓,狗珍剑,豬三個純虛函數(shù)掸宛,并且貓有1000種,狗有1000種招拙,豬有1000種唧瘾,在狗的類中,都有“吠”這個成員函數(shù)别凤,現(xiàn)在你想通過代碼讓所有的狗叫饰序,你不可能定義1000個狗的對象,那樣太麻煩了规哪,現(xiàn)在的問題是怎么做才能從3000個物種種得到你的1000只狗求豫,這樣就可以使用返回值來判斷,假設Dog是所有狗的基類,vec是所有3000種動物的集合)
Dog *g = new Dog;
foreach(auto i : vec)
{
if(dynamic_cast<i*>(g) != NULL)
g->func();
}
由于dynamic_cast
轉(zhuǎn)化失敗時诉稍,會返回空值蝠嘉,這樣不是繼承于Dog的類都不會調(diào)用到func()這個函數(shù),只有Dog派生出來的類才會調(diào)用杯巨。
擴展問題
- 如果不是多態(tài)會有什么情況蚤告?編譯錯誤
-
dynamic_cast
為什么只在多態(tài)的繼承關系才有效?由于運行時類型檢查需要運行時類型信息服爷,而這個信息存儲在類的虛函數(shù)表杜恰。
4. reinterpret_cast
修改了操作數(shù)類型,重新解釋了給出的對象的比特模型而沒有進行二進制轉(zhuǎn)換。
主要用于以下六種情況:
- 從指針類型到一個足夠大的整數(shù)類型
- 從整數(shù)類型或者枚舉類型到指針類型
- 從一個指向函數(shù)的指針到另一個不同類型的指向函數(shù)的指針
- 從一個指向?qū)ο蟮闹羔樀搅硪粋€不同類型的指向?qū)ο蟮闹羔?/li>
- 從一個指向類函數(shù)成員的指針到另一個指向不同類型的函數(shù)成員的指針
- 從一個指向類數(shù)據(jù)成員的指針到另一個指向不同類型的數(shù)據(jù)成員的指針
4.1 指針類型與整數(shù)類型的轉(zhuǎn)化
把指針值(地址)轉(zhuǎn)化成整數(shù)仍源,把整數(shù)轉(zhuǎn)化成指針值心褐。
#include <iostream>
#include <vector>
using namespace std;
void Func(){
cout << "Func" << endl;
}
int main() {
// 變量指針類型與整數(shù)類型轉(zhuǎn)化
{
int n = 100;
int addr = reinterpret_cast<int>(&n);
cout << "addr:" << hex << addr << dec << " "<< &n << endl;
int* b = reinterpret_cast<int*>(addr);
cout << "value:" << *b << endl;
}
// 函數(shù)指針類型與整數(shù)類型轉(zhuǎn)化
{
int f = reinterpret_cast<int>(Func);
typedef void (*pFunc)();
pFunc pf = reinterpret_cast<pFunc>(f);
pf();
}
}
4.2 函數(shù)指針的轉(zhuǎn)化
FuncNum
函數(shù)指針轉(zhuǎn)化成FuncAddr
,使用FuncAddr
的參數(shù)列表笼踩。
#include <iostream>
#include <vector>
using namespace std;
void FuncNum(int n){
cout << "num:" << n << endl;
}
void FuncAddr(int* p) {
cout << "addr:" << p << endl;
}
int main() {
int n = 10;
FuncNum(n);
FuncAddr(&n);
typedef void (*pfNum)(int n);
typedef void (*pfAddr)(int* n);
pfNum pfunc = FuncNum;
pfunc(n);
reinterpret_cast<pfAddr>(pfunc)(&n);
}
4.3 對象指針的轉(zhuǎn)化
A
類對象指針轉(zhuǎn)化成B
類的對象指針檬寂,使用B
類的成員函數(shù)。
#include <iostream>
#include <vector>
using namespace std;
class A{
public:
void Func(){
cout << "A" << endl;
}
};
class B {
public:
void Test() { cout << "B" << endl; }
};
int main() {
A* pA = new A;
pA->Func();
B* pB = reinterpret_cast<B*>(pA);
pB->Test();
}
4.4 類函數(shù)成員的轉(zhuǎn)化
A
類對象使用B
類的成員變量戳表。(A
類與B
類沒有任何關系桶至。)
#include <iostream>
#include <vector>
using namespace std;
class A{
public:
void Func(){
cout << "A" << endl;
}
};
class B {
public:
void Test() { cout << "B" << endl; }
};
int main() {
// 對象的函數(shù)指針
{
A a;
a.Func();
typedef void (A::*Func_t)();
Func_t pfTest = reinterpret_cast<Func_t>(&B::Test);
(a.*pfTest)();
}
// 對象指針的函數(shù)指針
{
A *pA = new A;
pA->Func();
typedef void (A::*Func_t)();
Func_t pfTest = reinterpret_cast<Func_t>(&B::Test);
(pA->*pfTest)();
}
}
4.5 類數(shù)據(jù)成員的轉(zhuǎn)化
#include <iostream>
#include <vector>
using namespace std;
class Test{
public:
Test(int data) : data(data) {}
private:
int data;
};
int main() {
Test test(0x61626364);
char* p = reinterpret_cast<char*>(&test);
for (int i = 0; i != sizeof(Test) / sizeof(char); i++) {
cout << p[i] << endl;
}
}
謹慎使用
reinterpret_cast
小結(jié)
No. | 轉(zhuǎn)換 | 轉(zhuǎn)換對象 | 作用 | 轉(zhuǎn)換時機 |
---|---|---|---|---|
1 | static_cast |
基本類型昼伴、指針、引用 | 實現(xiàn)傳統(tǒng)的小括號轉(zhuǎn)化功能 | 在編譯期間實現(xiàn)轉(zhuǎn)換 |
2 | const_cast |
const 類型的對象镣屹、指針圃郊、引用 |
移除變量const 限定 |
在編譯期間實現(xiàn)轉(zhuǎn)換 |
3 | dynamic_cast |
類的指針、類的引用或者void *
|
多態(tài)父類指針/引用轉(zhuǎn)化成子類指針/引用 | 在運行期間實現(xiàn)轉(zhuǎn)換女蜈,并可以返回轉(zhuǎn)換成功與否的標志/拋出異常 |
4 | reinterpret_cast |
指針持舆、引用、算術類型 | 萬能強制類型轉(zhuǎn)換 | 在編譯期間實現(xiàn)轉(zhuǎn)換 |
擴展閱讀
-
<<C++程序設計語言(第1-3部分)(原書第4版)>>
- 22.2.1
dynamic_cast
- 22.2.3
static_cast
和dynamic_cast
- 22.2.1