c++中的類型轉換

當您編寫C++程序時,務必確保它是類型安全的。 這意味着每個變量、函數參數和函數返回值存儲一種可接受的數據類型,涉及不同“有意義”類型的操作數,且不導致數據丟失、不正確的位組合解釋或內存損壞。 程序的類型安全的定義是從不顯式或隱式的把一種類型轉換成另一種類型。 但是,有時需要類型轉換,即使是不安全的轉換。 例如,在變量類型 int 中,您可能需要存儲浮點操作的結果,或者你可能需要項參數爲有符號 int 值的函數傳遞一個無符號 int 值。 兩個示例例舉了不安全的數據轉換,原因是它們可能會導致值的數據丟失或重新定義值。

當編譯器檢測到不安全的轉換時,將發出錯誤或警告。 錯誤使得編譯停止;警告允許繼續編譯,但在代碼中會指示出一個可能的錯誤。 但是,即使您的程序編譯後無警告,你的代碼中仍然可能包含會導致錯誤結果的隱式類型轉換。 在代碼中,類型錯誤也能由顯式轉換或強制轉換引入。

隱式類型轉換

當一個表達式包含不同的內置類型的操作數,且不存在顯式轉換,編譯器會使用內置的標準轉換來轉換一個操作數,使得操作數類型相匹配。 編譯器會嘗試按照一個定義良好的轉換序列轉換,直到有一個成功爲止。 如果所選的轉換是向上的,則編譯器不發出警告。 如果轉換是向下的,則編譯器發出關於數據可能丟失的警告。 無論實際數據丟失的發生是否取決於實際值,都建議您將此警告視爲錯誤。 如果涉及到用戶定義的類型,則編譯器嘗試使用您在類定義中指定的轉換。 如果找不到一個可接受的轉換,編譯器將發出錯誤,並停止編譯程序。 有關控制標準的轉換規則的詳細信息,請參閱 標準轉換. 有關用戶定義的轉換的詳細信息,請參閱 用戶定義的轉換。

擴大轉換 (提升)

在一個擴大轉換中,一個較小的變量的值在不丟失數據的情況下賦給一個更大的變量。 由於擴大轉換始終是安全的,編譯器在不提示的情況下執行它們,且不發出警告。 下面的轉換擴大轉換。

任何帶符號或無符號整型類型除 long long 和 __int64外。 double
bool 或 char 任何其他內置類型
short 或 wchar_t int, long, long long
int, long long long
float double

收縮轉換 (強制)

編譯器隱式執行收縮轉換,但是,它會發出可能丟失數據的警告。 要非常重視這些警告。 如果您確定數據丟失不會發生(因爲較小的變量始終可以容納較大的變量的值),則添加顯式強制轉換,編譯器將不再發出警告。 如果您不確定轉換是否安全,請在代碼中添加某種運行時檢查,以處理可能出現的數據丟失,而確保它不會使程序產生錯誤的結果。 有關如何處理這些方案的建議,請參見 如何:收縮轉換 (C++) 的句柄。

任何從浮點類型轉換爲整型類型都是收縮轉換,因爲浮點值的部分小數會被丟棄且丟失。

下面的代碼示例演示一些隱式收縮轉換和編譯器爲其發出的警告。

C++

int i = INT_MAX + 1; //warning C4307:'+':integral constant overflow
wchar_t wch = 'A'; //OK
char c = wch; // warning C4244:'initializing':conversion from 'wchar_t'
// to 'char', possible loss of data
unsigned char c2 = 0xfffe; //warning C4305:'initializing':truncation from
// 'int' to 'unsigned char'
int j = 1.9f; // warning C4244:'initializing':conversion from 'float' to
// 'int', possible loss of data
int k = 7.7; // warning C4244:'initializing':conversion from 'double' to
// 'int', possible loss of data

有符號 - 無符號轉換

有符號整型和它對應的無符號整型的數值部分總是一樣大的,但他們值轉換的位模式解釋是不同的。 下面的代碼示例演示的是相同的位模式被解釋爲有符號值和無符號值時會發生的情況。 存儲在 num 和 num2 的位模式不會像前面例子所展示的那樣改變。

C++

using namespace std;
unsigned short num = numeric_limits<unsigned short>::max(); // #include <limits>
short num2 = num;
cout << "unsigned val = " << num << " signed val = " << num2 << endl;
// Prints: unsigned val = 65535 signed val = -1

// Go the other way.
num2 = -1;
num = num2;
cout << "unsigned val = " << num << " signed val = " << num2 << endl;
// Prints: unsigned val = 65535 signed val = -1

請注意,值在這兩個方向中被重新詮釋。 如果你的程序會產生奇怪的結果,其中的值的符號似乎與預期的相反,請檢查有符號和無符號整數類型之間的隱式轉換。 在下面的例子中,當它的存儲在num時,表達式的結果(0 - 1)隱式的從 int 轉換到 unsigned int。 這使得位模式被重新解釋。

C++

unsigned int u3 = 0 - 1;
cout << u3 << endl; // prints 4294967295

編譯器不警告有符號和無符號整數類型之間的隱式轉換。 因此,建議您避免有符號和無符號之間的轉換。 如果您不能避免它們,然後在您的代碼添加運行時檢查,以檢測選定轉換值是否大於等於零或小於等於有符號類型的最大值。 此範圍內的值將從有符號數傳輸到無符號數或從無符號數傳輸到有符號數,而不必經過重新解釋。

指針轉換

在許多表達式,一個C風格的數組隱式把數組的第一個元素轉換爲一個指針,並且,轉換都是悄悄發生的。 雖然此方法很方便,但它也有潛在的錯誤。 例如,下面的設計不良的代碼示例看似荒謬,但它會在Visual C++的編譯並生成的結果'p'。 首先,“Help”字符串常量轉換爲一個指向數組的第一個元素 char*類型指針,該指針向後移動3個元素後,指向最後一個元素'P'。

C++

char* s = "Help" + 3;

顯式轉換(強制轉換)

使用轉換操作,您可以指示編譯器將一種類型的值轉換爲另一種類型。 在某些情況下即使兩個類型完全無關,編譯器也會引發錯誤,但是,其他情況下即使操作不是類型安全的,編譯器也不會引發錯誤。 使用強制轉換需謹慎,因爲從一種類型到另一個類型的任意轉換都是程序錯誤的潛在來源。 但是,強制轉換有時是需要的,且不是所有的強制轉換都一樣危險。 當你的代碼執行收縮轉換時,並且知道該轉換不會使程序產生錯誤的結果時,強制轉換是有效的。 實際上,這是在告訴編譯器,你知道你在幹什麼並且停止用警告干擾你的工作。 另一種強制轉換是從派生類指針到指針到基類指針的轉換。 另一個強制轉換的使用是將 const 變量傳遞給一個需要非 const 參數的函數。 大部分強制轉換操作都包含一定的風險。

在 C 樣式的程序中,相同 C 樣式的強制轉換運算符可以爲所有強制轉換使用。

C++

(int) x; // old-style cast, old-style syntax
int(x); // old-style cast, functional syntax

C 樣式的強制轉換運算符與運算符 () 一樣,因此在代碼中不顯眼,容易被忽略。 兩者都是不好的,因爲他們很難一眼就辨認出來或查找出來,並且他們是不同的,可包含static, const和 reinterpret_cast的任意組合。 指出舊式強制轉換實際上是困難且容易出錯。 基於以上這些原因,當需要強制轉換時,我們建議使用下面的 C++ 強制轉換運算符之一,在某些情況下,會加強類型安全,且更加明確的表示該程序的目的。

•static_cast,這種強制轉換只會在編譯時檢查。 如果編譯器檢測到您嘗試強制轉換完全不兼容的類型,則static_cast會返回錯誤。 您還可以使用它在基類指針和派生類指針之間強制轉換,但是,編譯器在無法分辨此類轉換在運行時是否是安全的。

C++

double d = 1.58947;
int i = d; // warning C4244 possible loss of data
int j = static_cast<int>(d); // No warning.
string s = static_cast<string>(d); // Error C2440:cannot convert from
// double to std:string

// No error but not necessarily safe.
Base b = new Base();
Derived
d2 = static_cast<Derived*>(b);

有關更多信息,請參見static_cast。

•爲了安全,dynamic_cast在運行時檢查基類指針和派生類指針之間的強制轉換。 dynamic_cast 是比 static_cast 更安全的強制類型轉換,但運行時檢查會帶來一些開銷。

C++

Base* b = new Base();

// Run-time check to determine whether b is actually a Derived
Derived
d3 = dynamic_cast<Derived*>(b);

// If b was originally a Derived*, then d3 is a valid pointer.
if(d3)
{
// Safe to call Derived method.
cout << d3->DoSomethingMore() << endl;
}
else
{
// Run-time check failed.
cout << "d3 is null" << endl;
}

//Output: d3 is null;

有關更多信息,請參見dynamic_cast。

•將const_cast轉換爲 const 的變量,或者將非 const 變量轉換爲 const。 通過使用這個操作符強制轉換 const 就像使用C樣式轉換一樣容易出錯,不同之處在於使用 const-cast 不太可能意外地執行轉換。 有時候你只能強制轉換 const 的變量,例如,傳遞 const 變量到一個非 const 參數的函數中。 下面的示例演示如何執行此操作。

C++

void Func(double& d) { ... }
void ConstCast()
{
const double pi = 3.14;
Func(const_cast<double&>(pi)); //No error.
}

有關更多信息,請參見const_cast。

•reinterpret_cast,例如 pointer 和 int的無關類型的轉換。

System_CAPS_ICON_note.jpg 說明

此強制轉換運算符不是通用的,因此,它不能保證可移植到其它編譯器。

下面的示例演示 reinterpret_cast 和 static_cast 之間的差異。

C++

const char str = "hello";
int i = static_cast<int>(str);//error C2440: 'static_cast' : cannot
// convert from 'const char
' to 'int'
int j = (int)str; // C-style cast. Did the programmer really intend
// to do this?
int k = reinterpret_cast<int>(str);// Programming intent is clear.
// However, it is not 64-bit safe.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章