typedef、using 類型別名
-
傳統類型別名使用關鍵字typedef:typedef sometype alias;
新標準使用類型別名聲明using:using alias = sometype;
-
如果某個類型別名指代的是複合類型或常量,那麼把它用到聲明語句裏就會產生意想不到的後果;如果直接使用別名進行替換,理解可能會發生偏差;見test_1
auto
- C++11標準引入了auto類型說明符,讓編譯器去分析表達式所屬的類型;auto通過初始值來推算變量的類型,那麼顯然auto定義的變量必須有初始值
- 使用auto可以在一條語句中聲明多個變量,因爲一條聲明語句只能夠有一個基本數據類型,所以該語句中所有變量的初始基本類型都必須一樣
- 編譯器推斷出的auto類型有時和初始類型並不完全一樣,編譯器會適當地改變結果類型使其更符合初始化規則
- 使用引用實際上使用的是引用的對象;特別是引用當作初始值時,真正參與初始化的其實是引用對象的值,此時編譯器以引用對象的類型作爲auto的類型;見test_2
- auto一般會忽略掉頂層const,同時底層const則會保留;如果希望推斷出的auto類型是一個頂層const,則需要明確指出;當設置一個類型爲auto的引用時,初始值中的頂層常量屬性仍然保留;見test_2
decltype
- 希望從表達式的類型推斷出要定義的變量類型,但不想用該表達式的值初始化變量;C++11標準引入decltype,通過編譯器分析表達式並得到其類型,卻不計算表達式的值
- 在decltype與引用一起使用時,引用表示本來的意義而不是指代其對象,這點與auto第4點處不同;例如:如果reference是一個引用,那麼decltype(reference)的結果就是引用類型,因此decltype(reference) reference_decltype變量就必須要初始化;見test_3
- decltype和auto另一處重要的區別是,decltype的結果類型與表達式的形式密切相關:對於decltype表達式,如果變量名加上了一對括號,得到的類型與不加括號時不同。如果decltype使用的是一個不加括號的變量,則得到的結果就是該變量的類型;如果給變量加上了一層或多層括號,編譯器會把它當作一個表達式。變量是一個種可以作爲賦值語句左值的特殊表達式,因此decltype就會得到引用類型;見test_3
- 切記: decltype((variable))的結果永遠是引用,而decltype(variable)結果只有當variable本身就是一個引用時纔是引用
static_cast、const_cast、reinterpret_cast、dynamic_cast 類型轉換
-
如果兩種類型可以相互轉換,那麼也可以稱這兩種類型是相關聯的
-
算術類型之間的隱式轉換的設計思路是:爲了計算過程中儘量避免損失精度,其中運算符的運算對象將轉換爲最寬類型
-
舊式顯式轉換包含:
- type (expr) // 函數形式的強制類型轉換
- (type) expr // C語言風格的強制類型轉換
根據所涉及的類型不同,舊式強制類型轉換分別具有與const_cast、static_cast或reinterpret_cast相似的行爲
-
新式顯示強制轉換:static_cast、const_cast、reinterpret_cast、dynamic_cast
- static_cast:
- 任何具有明確定義的類型轉換,只要不包含底層const,都可以使用static_cast;
- 對於需要將從大向小類型轉換的情況下,static_cast意味着編碼人員告訴編譯器,自己明確知道並不在乎潛在的精度損失,因此編譯器警告信息將會關閉
- static_cast對於編譯器無法自動執行的類型轉換也可以使用;例如可以找回void*指針的原始類型,但是必須保證轉換後的類型就是原指針的類型,否則使用轉換後的指針將產生未定義後果
- const_cast:
- const_cast只能改變運算對象的底層const屬性;將常量對象轉換爲非常量對象,即去掉const性質;該轉換旨在獲得對象的寫操作權限,此時要注意如果原對象本身就不是常量,那麼const_cast獲得寫權限是合法的行爲,如果對象是常量,則用const_cast執行寫操作就會產生未定義的後果
- 只有const_cast能改變表達式的常量屬性;使用其他形式的命名強制類型轉換改變表達式的常量屬性都將引發編譯器錯誤
- 不能用const_cast改變表達式的類型(不能幹static_cast的事情)
- reinterpret_cast:
- reinterpret_cast通常爲運算對象的位模式提供較低層次上的重新解釋
- 使用reinterpret_cast極其危險,它本質上依賴於編譯器,必須對涉及的類型和編譯器實現轉換的過程非常瞭解;例如通過reinterpret_cast將一個int *指針p轉換爲char *指針之後,後面編譯器將完全忘記p原有的int *屬性,將認定其值爲char *,使用不當將會引發糟糕的後果
- dynamic_cast:
- RTTI run-time type identification 運行時類型識別功能由兩個運算符實現:typeid 和 dynamic_cast
- dynamic_cast運算符用於將基類的指針或引用安全地轉換成派生類的指針或引用
- static_cast:
-
強烈建議程序員避免使用強制類型轉換,尤其是reinterpret_cast總是充滿了風險;const_cast在有重載函數的上下文使用無可厚非,但是在其他情況意味着程序存在某種設計缺陷;原則上static_cast、dynamic_cast也不應該頻繁使用;顯示強制轉換樣例見test_4
/**
* @Author: phd
* @Date: 2019/12/14
* @Site: github.com/phdsky
* @Description: NULL
*/
#include <iostream>
using namespace std;
void test_1() {
typedef char * char_pointer_alias;
const char_pointer_alias const_char_pointer = 0; // this is a const pointer pointing to char object
const char * const_char_pointer_another = 0; // this is a pointer pointing to const char object
}
void test_2() {
int value_int = 0, &reference_int = value_int;
auto auto_int = reference_int;
cout << typeid(auto_int).name() << endl;
const int const_int = 0, &reference_const_int = const_int;
auto auto_int_another = const_int;
cout << typeid(auto_int_another).name() << endl; // this is a int
const auto const_auto_int = const_int;
cout << typeid(const_auto_int).name() << endl; // this is a const int
auto &auto_reference = const_int;
// auto &auto_reference_another = 233; // error: non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'
const auto &auto_reference_other = 233;
}
void test_3() {
const int const_int = 0, &reference_const_int = const_int;
decltype(const_int) const_int_another = 233;
decltype(reference_const_int) reference_const_int_another = const_int_another;
// decltype(reference_const_int) reference_const_int_other; // error: declaration of reference variable 'reference_const_int_other' requires an initializer
int value_int = 666;
//decltype((value_int)) reference_int; // error: declaration of reference variable 'reference_int' requires an initializer
decltype(value_int) value_int_another;
}
void test_4() {
// static_cast
double double_value = 666.66;
int int_value = static_cast<int>(double_value) / 2;
cout << int_value << endl;
void *void_pointer = &double_value;
double *double_pointer = static_cast<double*>(void_pointer);
cout << *double_pointer << endl;
// const_cast
char char_value = 'p';
const char *const_char_pointer = &char_value;
// const_char_pointer = 'h'; // error: assigning to 'const char *' from incompatible type 'char'
char *char_pointer = const_cast<char*>(const_char_pointer);
*char_pointer = 'd';
cout << *const_char_pointer << ' ' << *char_pointer << endl;
// char *char_pointer_using_static_cast = static_cast<char*>(const_char_pointer); // error: static_cast from 'const char *' to 'char *' is not allowed
string static_cast_string = static_cast<string>(const_char_pointer);
// string const_cast_string = const_cast<string>(const_char_pointer); // error: const_cast to 'std::__1::string' (aka 'basic_string<char, char_traits<char>, allocator<char> >'), which is not a reference, pointer-to-object, or pointer-to-data-member
// reinterpret_cast
int *int_pointer = &int_value;
char *char_pointer_another = reinterpret_cast<char *>(int_pointer);
string string_value(char_pointer_another);
cout << *int_pointer << ' ' << *char_pointer_another << ' ' << string_value << endl;
// dynamic_cast
}
int main(int argc, char *argv[]) {
test_1();
test_2();
test_3();
test_4();
return 0;
}