類型轉換
C中的類型轉換
C語言中的類型轉換分爲隱式類型轉換和強制類型轉換兩種。
#include <stdio.h>
int main()
{
//隱式類型轉換
int i = 10;
double d = i;
printf("%d, %.2lf\n", i, d);
//強制類型轉換
i = (int)d;
printf("%d, %.2lf\n", i, d);
}
這種從C中繼承而來的類型轉換方式十分不直觀,可視性較差,不便於我們在發生錯誤時查找錯誤。
爲了解決這種問題,於是Cpp中新誕生了四種更加直觀更加安全的類型轉換操作。
Cpp中的類型轉換
static_cast
static_cast
用於非多態類型的轉換(靜態轉換),編譯器隱式執行的任何類型轉換都可用static_cast
,但它不能用於兩個不相關的類型進行轉換。
#include <iostream>
int main()
{
double d = 10.2;
int i = static_cast<int>(d);
std::cout << "d:" << d << " i:" << i << std::endl;
}
d:10.2 i:10
static_cast
是一個模板函數,如上使用就可以完成類型的轉換,
#include <iostream>
int main()
{
//double d = 10.2;
//int i = static_cast<int>(d);
//std::cout << "d:" << d << " i:" << i << std::endl;
int i = 10;
int *p = &i;
int address = static_cast<int>(p);
}
invalid static_cast from type 'int*' to type 'int'
但是如上的類型轉換就報錯了,說明static_cast
是無法將int*
轉爲int
的,如果兩個類型毫無關係則無法進行轉換。
reinterpret_cast
reinterpret_cast
是一種十分危險的類型轉換,它用於指針,引用以及可以容納指針的整數類型之間的轉換,之所以說它危險是你甚至可以將一個類型指針任意轉換爲其他類型的指針,比如將int*
轉換爲string*
,於是這個指針的命運由此就徹底改變了,使用不當就內存訪問越界程序崩潰。
#include <iostream>
int main()
{
int i = 10;
double d;
int* p = &i;
//將指針強轉爲整形
long long address = reinterpret_cast<long long>(p);
std::cout << p << " " << address;
}
0x61fe0c 6422028
const_cast
const_cast
可以完成從const
對象到non-const
對象的轉換.
#include <iostream>
int main()
{
const int i = 10;
std::cout << i << std::endl;
int& r = const_cast<int&>(i);
r = 11;
std::cout << i << std::endl;
std::cout << r << std::endl;
const int a = 2;
int *p = const_cast<int*>(&a);
*p = 3;
std::cout << a << std::endl;
std::cout << *p << std::endl;
}
經過類型強轉後我們發現確實可以更改指向常量的引用或指針了,但是通過輸出對比我們發現,引用和指針指向的數據被改變了但是原數據並沒有被改變,這是因爲編譯器的優化導致的,導致編譯器並沒有實際從內存中去取這塊內存,而是重新分配了一塊內存,我們可以使用volatile
關鍵字來防止編譯器的優化。
dynamic_cast
dynamic_cast
可以完成從父類到子類的指針或者引用的強制類型轉換。從子類到父類的轉換我們有切割幫我們進行隱式類型轉換,但是從父類到子類是不能自動轉換的,我們只能通過dynamic_cast
來強制轉換引用和指針,並且只能在有虛函數的類中進行轉換。dynamic_cast
在轉換前會先進行轉換實驗,如果能轉換成功則轉換,轉換不成功則返回0。
Cpp之所以可以完成類型的轉換,多虧與Cpp的RTTI機制,即運行時類型識別。雖然源文件中存儲着類型以及之間的繼承關係,但是其只用於編譯,在程序運行時是無法拿到這些信息,於是Cpp爲了可以在運行時獲取數據對象的類信息以及繼承關係便誕生了RTTI
,dynamic_cast
和typeid
是RTTI
的典型應用。
#include <iostream>
class Parent
{
public:
virtual void Func()
{
}
int s = 10;
};
class Son : public Parent
{
public:
int s = 11;
};
int main()
{
Parent* p = new Parent;
std::cout << p->s << std::endl;
Son* s = dynamic_cast<Son*>(p);
if(s == nullptr)
{
std::cout << "change error" << std::endl;
return -1;
}
std::cout << s->s << std::endl;
}
11
change error
explicit
這個參數之前已經在類和對象中講解過了,這個參數加在類的構造函數前用於禁用構造函數函數參數的隱式轉換。
未禁用:
#include <iostream>
class Test
{
public:
Test(int test)
:_test(test)
{
std::cout << "construct" << std::endl;
}
private:
int _test;
};
int main()
{
Test test = 10;
}
construct
禁用:
#include <iostream>
class Test
{
public:
explicit Test(int test)
:_test(test)
{
std::cout << "construct" << std::endl;
}
private:
int _test;
};
int main()
{
Test test = 10;//編譯不過
}