(1)C語言中的轉(zhuǎn)換
- 隱式轉(zhuǎn)換:由范圍小的類型轉(zhuǎn)換成范圍大的類型
- 格式:TypeName b = (TypeName)a;
int n=10;
float f=n;
cout << f <<endl;//10
float ff=10.2;
n=ff;//截取
cout << n <<endl;//10
int* p=&n;
void* v=p;//隱式轉(zhuǎn)換
p=(int*)v;//顯示轉(zhuǎn)換
(2)C++11提供了四種轉(zhuǎn)換方式
- 語法:
xxx_cast<要轉(zhuǎn)換的類型>(被轉(zhuǎn)換的變量) - 作用:(1)細化準換類型;(2)提供安全性的檢測;(3)在代碼中更加醒目
(3)static_cast
<1>說明
- 用于非多態(tài)類型之間的轉(zhuǎn)換,不提供運行時的檢查來確保轉(zhuǎn)換的安全性。
- c++ 的任何的隱式轉(zhuǎn)換都是使用
static_cast來實現(xiàn)。
<2>基本類型的轉(zhuǎn)換
char c='a';
cout << c <<endl;//a
cout <<(int)c <<endl;//97(C語言方式)
cout << static_cast<int>(c)<<endl;//97(C++中的轉(zhuǎn)換)
<3>指針的轉(zhuǎn)換
int* arr1=(int*)malloc(sizeof(int)*10);
int* arr2=static_cast<int*>(malloc(10*sizeof(int)));
<4>枚舉
enum BOOL{FALSE,TRUE};
//BOOL b=0;//不能從int轉(zhuǎn)換為BOOL
//BOOL b=(1==2);//不能從bool轉(zhuǎn)換為BOOL
BOOL bb=static_cast<BOOL>(0);
BOOL b=static_cast<BOOL>(1==2);
<5>基類和子類之間指針和引用的轉(zhuǎn)換
- 上行轉(zhuǎn)換
上行轉(zhuǎn)換,把子類的指針或引用轉(zhuǎn)換成父類,這種轉(zhuǎn)換是安全的(通常使用默認轉(zhuǎn)換)。
class Base{};
class Sub: public Base{};
// 上行 Sub -> Base
//編譯通過,安全
Sub sub;
Base *base_ptr = static_cast<Base*>(new Sub);
通常使用隱式轉(zhuǎn)換
Base* pb=new Sub;
- 下行轉(zhuǎn)換
下行轉(zhuǎn)換,把父類的指針或引用轉(zhuǎn)換成子類,這種轉(zhuǎn)換是不安全的,也需要程序員來保證(通常使用dynamic_cast)。
// 下行 Base -> Sub
//編譯通過,不安全,需要使用dynamic_cast
Base base;
Sub *sub_ptr = static_cast<Sub*>(&base);
(4)const_cast
<1>說明
- 去掉
const修飾的不可修改特性,只是在const_cast中去掉該特性,其他時候還是const變量
<2>基礎(chǔ)類型變量
- 在<>里面不能用基本類型,只能用指針和引用
- 基礎(chǔ)類型const常量在使用時展開操作,類似在宏定義中展開(編譯時之間進行值的替換),所以值沒有變.
const int a=10;
//const_cast<int>(a)=11;//注意:在<>里面不能用基本類型,只能用指針和引用
const_cast<int&>(a)=12;//已經(jīng)改變a所在內(nèi)存的值
基礎(chǔ)類型const常量在使用時展開操作,類似在宏定義中展開,所以a的值沒有變
cout << &a << "," << a <<endl;//0xabcd,10 // 常量展開,看不到改變
const int* pa=&a;
cout << pa << "," << *pa <<endl;//0xabcd,12
*const_cast<int*>(pa)=11;
cout << &a << "," << a <<endl;//0xabcd,10
cout << pa << "," << *pa <<endl;//0xabcd,11
<3>string類型的變量
const string s("abcd");
//s+="def";//報錯
cout << s <<endl;//abcd
const_cast<string&>(s) +="def";
cout << s <<endl;//abcddef
<4>類類型的對象
class Simple{
int n;
public:
Simple(int n):n(n){}
void Set(int n){this->n=n;}
int Get()const{return n;}
};
const Simple s1(100);
cout << s1.Get() << endl;//100
const_cast<Simple&>(s1).Set(110);//const對象只能訪問const成員函數(shù),去掉s1的const屬性,<>里要用引用
cout << s1.Get() << endl;//110
const Simple* ps1=&s1;
cout << ps1->Get() << endl;//110
const_cast<Simple*>(ps1)->Set(120);
cout << ps1->Get() <<endl;//120
<5>const成員函數(shù)中修改成員變量
- 在
const函數(shù)中所有成員變量都有const屬性。 -
const成員函數(shù)的本質(zhì):在const函數(shù)中this指針是const類型,所以說所有成員不能修改。 - const成員函數(shù)中修改成員變量有3中方法:
(1)把需要修改的成員變量const_cast<>()轉(zhuǎn)成非const類型
(2)把this使用const_cast<>()轉(zhuǎn)換成非const類型,然后修改成員變量
(3)mutable的成員變量可以在const函數(shù)中修改
提供一個打印出Set/Get次數(shù)的函數(shù)
class Integer{
int n;
int setter;
mutable int getter;//法三
public:
Integer(int n):n(n),setter(0),getter(0){}
void Set(int n){//非const成員函數(shù)中可以直接修改成員變量的值
++setter;
this->n=n;
}
int Get()const{//const成員函數(shù)不能直接修改成員變量的值
//++const_cast<int&>(getter);//法一
//++(const_cast<Integer*>(this)->getter);//法二
++getter;
return n;
}
(5)dynamic_cast
用于類的指針、類的引用或者void *轉(zhuǎn)化,dynamic_cast只在多態(tài)有效。
<1>說明
主要用于以下種情況:
- 上行轉(zhuǎn)換、下行轉(zhuǎn)換、交叉轉(zhuǎn)換
<2>上行轉(zhuǎn)換
- 上行轉(zhuǎn)換:把子類的指針或引用轉(zhuǎn)換成父類,與
static_cast相同。
<3>下行轉(zhuǎn)換
- 下行轉(zhuǎn)換:把父類的指針或引用轉(zhuǎn)換成子類,
dynamic_cast具有類型檢查的功能,比static_cast安全。使用時的2個條件:
(1)父類指針必須指向當前類型的子類對象;
(2)至少有一個虛函數(shù)
class Animal{
string name;
public:
Animal(const string& name):name(name){}
virtual string Feature()const=0;//虛函數(shù)或者純虛函數(shù)都行
//virtual string Feature()const{return " ";}
const string& GetName()const{return name;}
};
class Cat:public Animal{
int n;
public:
Cat(const string& name,int n):Animal(name),n(n){}//注意:對于非默認構(gòu)造函數(shù)的編寫
string Feature()const{return "抓老鼠";}
int GetMouseNum()const{return n;}
};
class Dog:public Animal{
public:
Dog(const string& name):Animal(name){}
string Feature()const{return "看門";}
};
void Display(Animal** arr,int n){//或者Animal** arr
for(int i=0;i<n;++i){
cout <<arr[i]->GetName()<<arr[i]->Feature();
// 下行轉(zhuǎn)換,把父類指針轉(zhuǎn)換成子類指針。1.父類指針必須指向當前類型的子類對象。2.必須有虛函數(shù)。
Cat* cat=dynamic_cast<Cat*>(arr[i]);//cat有兩中結(jié)果:成功的話就是Cat*
if(NULL!=cat){//失敗的的話就是NULL
cout << "抓了"<<cat->GetMouseNum() << "只老鼠";
}
cout << endl;
}
}
int main(){
Animal* arr[]={new Cat("貓",5),new Dog("狗")};
Display(arr,2);
}
如果時指針,進行正確的轉(zhuǎn)換,獲得對應(yīng)的值;否則返回NULL,如果是引用,則在運行時就會拋出異常;
- 向下轉(zhuǎn)換的成功與否還與將要轉(zhuǎn)換的類型有關(guān),即要轉(zhuǎn)換的指針指向的對象的實際類型與轉(zhuǎn)換以后的對象類型一定要相同,否則轉(zhuǎn)換失敗。
<4>交叉轉(zhuǎn)換
交叉轉(zhuǎn)換,兄弟之間指針轉(zhuǎn)換
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 << "Derive2" << endl; }
};
int main() {
Derive1* pD1 = new Derive1;
pD1->Print();//Derive1
Derive2 *pD2 = dynamic_cast<Derive2*>(pD1);
pD2->Print();//Derive2
}
dynamic_cast為什么只在多態(tài)的繼承關(guān)系才有效?由于運行時類型檢查需要運行時類型信息,而這個信息存儲在類的虛函數(shù)表。
(6)reinterpret_cast
- 改變指針或引用的類型、
- 對象指針的轉(zhuǎn)化(A類對象指針轉(zhuǎn)化成B類的對象指針,使用B類的成員函數(shù))
<1>將整型轉(zhuǎn)換為指針或引用類型。
int n=100;
//void* p=static_cast<void*>(n);
void* p=reinterpret_cast<void*>(n);
cout << p << endl;
<2>將指針或引用轉(zhuǎn)換為一個足夠長度的整型
// 精度缺失不能轉(zhuǎn)換
// n = reinterpret_cast<int>(p);
// n = (int)p;
long addr = reinterpret_cast<long>(p);
cout << addr << endl;
<3>函數(shù)指針與指針的轉(zhuǎn)換
void Func(){
cout << __func__ <<endl;
cout << __cplusplus <<endl;//輸出編譯器版本
}
//void* pf=static_cast<void*>(Func);
void* pf=reinterpret_cast<void*>(Func);
typedef void (*pfunc)();
void (*func)()=reinterpret_cast<void(*)()>(pf);
void (*func)()=reinterpret_cast<pfunc>(pf);//和上面一樣
<4>打印地址
- 打印基本類型變量和數(shù)組的地址
int n=10;
int* p=&n;
cout << p <<endl;
int arr[]={1,2,4};
cout << arr <<endl;
- 打印字符串數(shù)組地址
需要用static_cast
const char* s="hello";
cout << s <<" "<<static_cast<const void*>(s)<<endl;//直接打印會打印出字符串的值
printf("%s\n",s);
printf("%p\n",s);
- 打印函數(shù)地址
需要用reinterprete_cast
void Func(){cout << __cplusplus <<endl;}//c++編譯器的版本
//cout << Func <<endl;//直接打印函數(shù)名為1
cout << (void*)Func <<endl;//C語言方式
cout << reinterpret_cast<void*>(&Func) <<endl;
printf("%p\n",Func);
<5>類中的偏移量和地址
- 【&類名::成員變量】: 獲取成員變量在對象中的偏移量
- 【&對象.成員變量】: 獲取成員變量的地址
class Point3D{
public:
int x,y,z;
Point3D(int x,int y,int z):x(x),y(y),z(z){}
void Print()const{
cout << "(" <<x << "," << y <<","<<z<<")"<<endl;
}
};
int main(){
Point3D point(1,2,3);
point.Print();
1.成員變量在對象中的偏移量
printf("%d %d %d\n",&Point3D::x,&Point3D::y,&Point3D::z);//必須加&,因為不是static成員變量,沒有這種訪問方式
結(jié)果:0 4 8
cout << &Point3D::x <<' '<<&Point3D::y <<' '<<&Point3D::z <<endl;
結(jié)果:1 1 1(沒啥用)
所以:要想打印對象中成員變量的偏移量需要用printf
2.對象的地址
printf("%p\n",&point);
cout << reinterpret_cast<void*>(&point)<<endl;
輸出:0061FF04
3.成員變量的地址
printf("%p %p %p\n",&point.x,&point.y,&point.z);
cout << reinterpret_cast<void*>(&point.x)<<endl;
輸出:0061FF04 0061FF08 0061FF0C
所以:成員變量的地址等于對象地址加上成員變量的偏移量
4.成員函數(shù)的地址
cout <<(void*)&Point3D::Print<<endl;
cout << reinterpret_cast<void*>(&Point3D::Print)<<endl;//獲取成員函數(shù)地址必須加&
cout << reinterpret_cast<void*>(&point.Print)<<endl;
(7)總結(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 | 指針、引用、算術(shù)類型 | 萬能強制類型轉(zhuǎn)換 | 在編譯期間實現(xiàn)轉(zhuǎn)換 |