C11新特性(部分)
C11部分新特性
- 1.類型推導
- 1.1 auto
- 1.1.1 auto的基本使用
- 1.1.2 auto的推導規則
- 1.1.3 auto的限制
- 1.2 decltype
- 2.nullptr-指針空值
- 3.基于范圍的for循環
- 4.typedef與using
- 4.1typedef的語法和使用場景
- 4.2 using的語法與使用場景
- 5.新增容器
- 5.1 std::array
- 5.2 std::forward_list
- 5.3 無序容器
- 5.4 元組 std::tuple
1.類型推導
C11引入了auto和decltype這兩個關鍵字實現類型推導,可以獲取復雜類型。
1.1 auto
1.1.1 auto的基本使用
auto為類型指示符 auto定義的變量,可以根絕初始化的值,在編譯時推導出變量名的類型。
int main()
{
auto x = 5; //ok x是int類型
auto pi = new auto(1); //ok pi是int *型
const auto *p = &x,u=6; //ok p是const int *型,u是const int型
static auto dx = 3.4; //ok dx是double類型
}
使用auto必須給定初始化值,如果沒有給定無法推導出類型:
auto s;// error 沒有初始化值 無法推導出s的類型。
C11中auto不再表示存儲類型指示符:
auto int b;//error C11中auto不再表示存儲類型指示符。
此處u必須給定一個初始值才可以 編譯通過,不然會報錯。
當我們給定u與x的類型不同的值時也會報錯,是因為這時候產生了二異性。
從上面的這些例子看出,auto并不是一個實際的類型聲明。
使用auto聲明的變量必須要有初始化值,才可以讓編譯器推斷出它的實際類型,在編譯的時候將auto替換為真正的數據類型。
1.1.2 auto的推導規則
auto可以與指針,引用結合使用,還可以有cv限定符。
int main()
{
int x =0;
auto *ip = &x; // ok ip ->int*,auto被推導為int
auto xp = &x; // ok xp -> int*, auto被推導為int*
auto &c = x; // ok c -> int &,auto被推導為int
auto d = x; // ok d -> int , auto被推導為int
const auto e = x; // ok e ->const int;
auto f = e; // ok f -> int;
const auto &g = x; // ok g -> const int &
auto & h = g; // ok h -> const int &
}
當不聲明為指針或引用時,auto的推導結果和初始化表達式拋棄引用和cv限定符后類型一致。
當聲明為指針或引用時,auto的推導結果將保持初始化表達式的cv屬性。
1.1.3 auto的限制
代碼測試:
代碼測試:
auto 不能用于函數傳參,因此下面的做法是無法通過編譯的(考慮重載的問題,我們應該使用模板):
int add(auto x, auto y);1.2 decltype
decltype是用來在編譯時推導出一個表達式的類型。
它的用法和 sizeof 很相似:
類似于sizeof,decltype的推導過程是在編譯期完成的,編譯器分析表達式并得到它的類型,卻不實際計算表達式的值。
auto x = 10; //x=int decltype(x)y; //y=int decltype(x + y) z;//z=int代碼測試:
不去調動Add 只是計算表達式判斷類型
代碼測試:
2.nullptr-指針空值
當我們初始化一個指針時是將其指向一個""空"的位置,大部分情況下我們使用的都是0或NULL。因此在大多數的代碼中,我們常常能看見指針初始化的語法如下:
int *p = 0; int *p = NULL;通過上面對空指針的定義,我們不難看出NULL可能被定義為字面常量0,或者是定義為無類型指針(void *) 0常量。
我們在使用空值的指針時,都不可避免地會遇到一些麻煩:
這里我們期望調用的函數并沒有被調用,這個問題是由于0的二義性。
為了解決上述問題,就在C11中引入了nullptr。
C11標準中,將nullptr定義為一個指針空值的常量。
當使用nullptr后:
這里運行結果,就符合用戶的期望。
所以,以后書寫代碼的時候如果遇到NULL,我們就可以將NULL替換為nullptr。
nullptr與nullptr_t:
nullptr_t在C11中被定義為指針空值類型。 我們可以通過nullptr_t來聲明一個指針空值類型的變量。
在使用的時候對于nullptr_r,我們需要#include,而nullptr不需要,直接使用即可。所以我們也可以認為nullptr是關鍵字,而nullptr_r是推導而來的。
注意:
1、nullptr 是C11新引入的關鍵字,是一個所謂"指針空值類型"的常量,
在C++程序中直接使用。
2、在C11中,sizeof(nullptr)與sizeof(void*)0)所占的字節數相同都為4或8**(對應32位和64位)。
3、為了提高代碼的健壯性,在后續表示指針空值時建議最好使用nullptr。
3.基于范圍的for循環
在C98中,不同的容器和數組,遍歷的方法不盡相同,寫法不統一,也不夠簡潔。C++11引入了基于范圍的迭代寫法,我們擁有了能夠寫出像 Python 一樣簡潔的循環語句。
C11基于范圍的for循環以統一、簡潔的方式來遍歷容器和數組,用起來更方便。
首先明確什么是容器:能夠容納其他元素的元素或者容納其他對象的對象。
基于范圍的for循環一般格式:
for(ElemType val: array)
{
…//statement 循環體
}
ElemType:是范圍變量的數據類型。
它必須與數組(容器)元素的數據類型一樣,或者是數組元素可以自動轉換過來的類型。
val :是范圍變量的名稱。
該變量將在循環迭代期間依次接收數組中的元素值。在迭代期間,接收的是本次迭代次數的元素值。
array:是要讓該循環進行處理的數組(容器)的名稱。該循環將對數組中的每個元素迭代一次。
statement:是在每次循環迭代期間要執行的語句。
我們以最常用的 std::vector 遍歷說明。
未使用基于范圍的for循環,代碼如下:
使用基于范圍的for循環后,代碼如下:
// & 啟用了引用 for(auto &i : arr) { std::cout << i << std::endl; }由上述例子我們可以明確觀察到,使用基于范圍的for循環后會變得簡單明了。
并且我們需要記住,基于范圍的for一定是一個集合。
4.typedef與using
4.1typedef的語法和使用場景
typedef是C/C++語言中保留的關鍵字,用來定義一種數據類型的別名。需要注意的是typedef并沒有創建新的類型,只是指定了一個類型的別名而已。
typedef定義的類型的作用域只在該語句的作用域之內, 也就是說如果typedef定義在一個函數體內,那么它的作用域就是這個函數。
typedef經常使用的場景包括以下幾種:
4.2 using的語法與使用場景
C++11 中擴展了using的使用場景(C++11之前using主要用來引入命名空間名字 如:using namespace std;),可以使用using定義類型的別名:
使用語法如下:
using 別名 = xxx(類型);using聲明別名的順序和typedef是正好相反:typedef首先是類型,接著是別名,而using使用別名作為左側的參數,之后才是右側的類型,例如上面的類型定義:
typedef int points;using points = int; //等價的寫法定義諸如函數指針等類型時,使用using的方式更加自然和易讀:
typedef void (*FP) (int, const std::string&); using FP = void (*) (int, const std::string&); //等價的using別名using可以在模板別名中使用,但是typedef不可以
template <typename T> using Vec = MyVector<T, MyAlloc<T>>;// usage Vec<int> vec;歸根到底就是一句話,在C++11中,請使用using,而非typedef,它可以完成typedef能完成的,并且可以做的更多。
5.新增容器
5.1 std::array
std::array 保存在棧內存中,相比堆內存中的 std::vector,我們能夠靈活的訪問這里面的元素,從而獲得更高的性能。
std::array 會在編譯時創建一個固定大小的數組,std::array 不能夠被隱式的轉換成指針,使用 std::array只需指定其類型和大小即可:
std::array<int, 4> arr= {1,2,3,4}; int len = 4; std::array<int, len> arr = {1,2,3,4}; //error 數組大小參數必須是常量表達式當我們開始用上了 std::array 時,可能要將其兼容 C 風格的接口,這里有三種做法:
void foo(int *p, int len) {return; }std::array<int 4> arr = {1,2,3,4};// C 風格接口傳參 // foo(arr, arr.size()); // error, 無法隱式轉換 foo(&arr[0], arr.size()); foo(arr.data(), arr.size());// 使用 `std::sort` std::sort(arr.begin(), arr.end());5.2 std::forward_list
std::forward_list 是一個列表容器,使用方法和 std::list 基本類似。
和 std::list 的雙向鏈表的實現不同,std::forward_list 使用單向鏈表進行實現,提供了 O(1) 復雜度的元素插入,不支持快速隨機訪問(這也是鏈表的特點),也是標準庫容器中唯一一個不提供 size() 方法的容器。當不需要雙向迭代時,具有比 std::list 更高的空間利用率。
5.3 無序容器
C++11 引入了兩組無序容器:
std::unordered_map/std::unordered_multimap std::unordered_set/std::unordered_multiset。無序容器中的元素是不進行排序的,內部通過 Hash 表實現,插入和搜索元素的平均復雜度為 O(constant)。
5.4 元組 std::tuple
元組的使用有三個核心的函數:
合并兩個元組,可以通過 std::tuple_cat 來實現。
auto new_tuple = std::tuple_cat(get_student(1), std::move(t));參考鏈接: C11新特性
總結
以上是生活随笔為你收集整理的C11新特性(部分)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Window10彻底卸载应用商店
- 下一篇: 2017 实习面试问题总结(阿里、头条、