文章目錄
- C++11簡介
- 列表初始化
- 1. {}初始化
- 2. initializer_list容器
- initializer_list的使用場景
- 聲明
- 1. auto
- 2. decltype
- 3. nullptr
- STL中的變化
- 1. 新容器
- array容器
- forward_list容器
- unordered_map和unordered_set容器
- 2. 新接口
C++11簡介
- C++98/03:在2003年C++標準委員會曾經提交了一份技術勘誤表(簡稱TC1),使得C++03這個名字已經取代了C++98稱為C++11之前的最新C++標準名稱。不過由于C++03(TC1)主要是對C++98標準中的漏洞進行修復,語言的核心部分則沒有改動,因此人們習慣性的把兩個標準合并稱為C++98/03標準。
- 從C++0x到C++11:C++標準10年磨一劍,第二個真正意義上的標準珊珊來遲。相比于C++98/03,C++11則帶來了數量可觀的變化,其中包含了約140個新特性,以及對C++03標準中約600個缺陷的修正,這使得C++11更像是從C++98/03中孕育出的一種新語言。
C++11能更好地用于系統開發和庫開發、語法更加泛華和簡單化、更加穩定和安全,不僅功能更強大,而且能提升程序員的開發效率,公司實際項目開發中也用得比較多,所以我們要作為一個重點去學習。
C++11全部特性參考C++官網:https://en.cppreference.com/w/cpp/11.html
小插曲:
1998年是C++標準委員會成立的第一年,本來計劃以后每5年視實際需要更新一次標準,C++國際標準委員會在研究C++ 03的下一個版本的時候,一開始計劃是2007年發布,所以最初這個標準叫C++ 07。但是到06年的時候,官方覺得2007年肯定完不成C++ 07,而且官方覺得2008年可能也完不成。最后干脆叫C++ 0x。x的意思是不知道到底能在07還是08還是09年完成。結果2010年的時候也沒完成,最后在2011年終于完成了C++標準。所以最終定名為C++11。
列表初始化
1. {}初始化
C++98中?般數組和結構體可以?{}進?初始化。
struct Point
{int _x;int _y;
};
int main()
{int array1[] = { 1, 2, 3, 4, 5 };int array2[5] = { 0 };Point p = { 1, 2 };return 0;
}
C++11擴大了{}
初始化的使用范圍,內置類型和用戶自定義的類型皆可用{}
初始化,并且可以不寫=
struct Point
{int _x;int _y;
};
int main()
{int x1 = 1;int x2{ 2 };int array1[]{ 1, 2, 3, 4, 5 };int array2[5]{ 0 };Point p{ 1, 2 };// C++11中列表初始化也可以適用于new表達式中return 0;
}
注意:但不建議不寫=
,因為影響代碼可讀性
創建對象時也可以使用列表初始化方式調用構造函數初始化
struct Point
{//explicit Point(int x, int y)Point(int x, int y):_x(x),_y(y){cout << "Point(int x, int y)" << endl;}int _x;int _y;
};int main()
{// 本質都是調用構造函數Point p0(0, 0);Point p1 = { 1,1 }; // 多參數構造函數隱式類型轉換const Point& r = { 3,3 };// C++11中列表初始化也可以適用于new表達式中(本質也是調用構造函數)int* ptr1 = new int[3]{ 1,2,3 };Point* ptr2 = new Point[2]{p0,p1};Point* ptr3 = new Point[2]{ {0,0},{1,1} };return 0;
}
2. initializer_list容器
std::initializer_list的介紹文檔
C++11中新增了initializer_list容器,該容器沒有提供過多的成員函數。
- begin和end函數,用于支持迭代器遍歷
- size函數支持獲取容器中的元素個數。
其實,這個類的本質是底層開?個數組,將數據拷貝過來,std::initializer_list內部有兩個指針分別指向數組的開始和結束。
std::initializer_list是什么類型呢?
initializer_list本質就是一個大括號括起來的列表,如果用auto關鍵字定義一個變量來接收一個大括號括起來的列表,然后以typeid(變量名).name()的方式查看該變量的類型,此時會發現該變量的類型就是initializer_list。
int main()
{auto il = { 10, 20, 30 };cout << typeid(il).name() << endl;return 0;
}
initializer_list的使用場景
std::initializer_list一般是作為構造函數的參數,C++11對STL中的不少容器就增加了std::initializer_list作為參數的構造函數,這樣初始化容器對象就更方便了。也可以作為operator=
的參數,這樣就可以用大括號賦值。
initializer_list功能:是為了讓其他容器支持列表初始化的
一些容器之所以支持使用列表進行初始化,根本原因是因為C++11給這些容器都增加了一個構造函數,這個構造函數就是以initializer_list作為參數的。
當用列表對容器進行初始化時,這個列表被識別成initializer_list類型,于是就會調用這個新增的構造函數對該容器進行初始化。
這個新增的構造函數要做的就是遍歷initializer_list中的元素,然后將這些元素依次插入到要初始化的容器當中即可。
聲明
1. auto
在C++98中auto是一個存儲類型的說明符,表明變量是局部自動存儲類型,但是局部域中定義局
部的變量默認就是自動存儲類型,所以auto就沒什么價值了。C++11中廢棄auto原來的用法,將其用于實現自動類型推斷。這樣要求必須進行顯示初始化,讓編譯器將定義對象的類型設置為初始化值的類型。
注意:auto只可以用于定義時聲明,不可單獨用來聲明一個變量,因為auto需要用左邊的值的類型推導
auto a = 10;
auto b;//不可單獨用來聲明一個變量
2. decltype
關鍵字decltype可以將變量的類型聲明為表達式指定的類型。
template<class Func>
class B
{
private:Func _f;
};int main()
{auto p = &i;auto pf = malloc;cout << typeid(p).name() << endl;cout << typeid(pf).name() << endl;//decltype推出對象的類型可以定義變量decltype(pf) pf2;//decltype推出對象的類型作為模板實參B<decltype(pf)> bb1;const int x = 1;double y = 2.2;B<decltype(x * y)> bb2;return 0;
}
decltype
和typeid(變量名).name()
的區別:
typeid(變量名).name()
推出類型是一個字符串,只能看不能用decltype
推出對象的類型可以定義變量,或者作為模板實參
3. nullptr
由于C++中NULL被定義成字面量0,這樣就可能回帶來一些問題,因為0既能指針常量,又能表示整形常量。所以出于清晰和安全的角度考慮,C++11中新增了nullptr,用于表示空指針。
/* Define NULL pointer value */
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else /* __cplusplus */
#define NULL ((void *)0)
#endif /* __cplusplus */
#endif /* NULL */
在大部分情況下使用NULL不會存在什么問題,但是在某些極端場景下就可能會導致匹配錯誤。
當參數為整數和整型指針函數重載時,調用時實參傳NULL和nullptr是有區別的:
void f(int arg)
{cout << "void f(int arg)" << endl;
}
void f(int* arg)
{cout << "void f(int* arg)" << endl;
}
int main()
{f(NULL); //void f(int arg)f(nullptr); //void f(int* arg)return 0;
}
因此,在C++中設置空指針使用nullptr
STL中的變化
1. 新容器
C++11中新增了四個容器,分別是array、forward_list、unordered_map和unordered_set。
array容器
array容器本質就是一個靜態數組(固定大小的數組)。
array容器有兩個模板參數,第一個模板參數代表的是存儲的類型,第二個模板參數是一個非類型模板參數,代表的是數組中可存儲元素的個數。
定義方式:
int main()
{array<int, 5> a;//定義一個可存儲5個int類型元素的array容器array<char, 10> s;////定義一個可存儲10個char類型元素的array容器
}
array其實與普通數組沒有什么區別,唯一一個區別就是:array具有嚴格的越界檢查,因為重載operator時采用了斷言檢查。
但vector似乎也不乏這個功能,甚至還有其他更加強大的功能,那有什么必要用array呢?
個人認為這個容器還是比較多余的,不推薦使用。
forward_list容器
forward_list容器本質是一個單鏈表。
我們知道,單鏈表相比于list(帶頭雙向循環鏈表)是非常雞肋的,唯一的優點就是,減少空間消耗,但微乎其微。
因此forward_list也很少使用。
unordered_map和unordered_set容器
這兩個容器是非常有價值的,筆者的其他博客對這兩個容器進行了詳細介紹。
【C++篇】STL的關聯容器:unordered_map和unordered_set(上篇):哈希表的模擬實現
2. 新接口
-
對于容器的迭代器:
個人認為單獨為const迭代器提供cbegin和cend接口是沒有必要的,因為完全可以用begin和end重載它們。 -
所有容器均支持用
{}
列表初始化構造函數
當然,這是依仗initializer_list的功勞。 -
所有容器新增了emplack系列
功能和insert差不多,但性能會略高一點,后續右值引用會詳細講解。 -
所有容器新增了移動構造和移動賦值
這里可以大大提升拷貝構造的性能,也是右值引用帶來的功勞。
右值引用究竟何方神圣?竟有如此大的威能?
我們下篇見分曉!