1.列表初始化
1.1擴展了初始化列表(用{}括起來的列表)的使用范圍
使其可用於所有的內置類型和用戶自定義的類型,使用初始化列表可以加=(沒區別),也可不加。例如以下用法:
int a = { 1 }; //多此一舉 int b{ 2 }; int c{ 1 + 2 }; int d{ a + b }; vector<int> v = { 1, 2, 3, 4 }; vector<string> vs = { "string", "vector" }; int *arr = new int[]{1, 2, 3, 4}; map<int, int> mymap{ { 1, 1 }, { 2, 2 } };
1.2自定義類型的列表初始化
class Date { public: Date(int year, int month, int day) :_year(year) , _month(month) , _day(day) {} private: int _year; int _month; int _day; }; int main() { Date d1{ 2019, 6, 26 }; //標准庫支持單個對象使用列表進行初始化 return 0; }
對於多個對象的列表初始化:
首先認識一個新的數據結構 initializer_list
#include <initializer_list> int main() { initializer_list<int> initlist{ 1, 2, 3, 4 }; auto it = initlist.begin(); for (; it != initlist.end(); ++it) { cout << *it << " "; } cout << endl; cout << initlist.size() << endl; return 0; }
initializer_list支持迭代器,size()接口
在多個對象的列表初始化時,C++11是用了initializer_list,必須包含一個帶有initializer_list參數的構造函數,類似:
template <class T> class Vector { public: Vector(initializer_list<T> l) //包含一個帶有initializer_list參數的構造函數 :_capacity(l.size()) , _size(0) { _arr = new T[_capacity]; for (const auto& it : l) { _arr[_size++] = it; } } size_t size() { return _size; } T& operator[](size_t i) { return *(_arr + i); } private: T *_arr; size_t _size; size_t _capacity; }; int main() { Vector<int> V{ 1, 2, 3, 4 }; //用{}內容構建了一個initializer_list對象 for (size_t i = 0; i < V.size(); ++i) { cout << V[i] << " "; } cout << endl; return 0; }
2.變量類型推導
2.1 auto
當變量類型寫起太復雜或不知道實際類型時,例如:
int main() { short a = 32760; short b = 32765; //若指定c的類型為short,則會造成數據丟失, //使用auto,讓編譯器自己推演c的實際類型,就不會存在問題 auto c = a + b; //c的類型為int unordered_map<int, int> mymap{ { 1, 1 }, { 2, 2 } }; //unordered_map<int, int>::iterator it = mymap.begin(); auto it = mymap.begin(); //使用auto可避免如上復雜的寫法 return 0; }
2.2 decltype類型推導
auto在使用時必須對變量進行初始化,編譯器才能推演出變量的實際類型。
使用decltype可根據表達式的實際類型推演出定義變量時所用的類型,例如:
根據類型推導定義變量:
int a = 1; int b = 2; decltype(a + b) c; //推演為int c;
推演函數返回值類型:
void *func() { return (void *)0; } int main() { //不帶參數列表是,返回函數的類型 cout << typeid(decltype(func)).name() << endl; //void * __cdecl(void) //帶參數列表時,返回函數返回值類型 cout << typeid(decltype(func())).name() << endl; //void * return 0; }
decltype使用RTTI(運行時類型識別),C++98也支持了RTTI,在typeid和dynamic_cast中有使用。運行時類型識別會降低程序運行效率。
3.基於范圍for的循環
vector<int> v{ 1, 2, 3, 4 }; for (auto it : v) { //... }
4.final和override
用final修飾的類不可被繼承:
class A final { public: A(int data = 1) :_data(data) {} void print() { cout << _data << endl; } private: int _data; }; class B : public A //不能將“final”類類型用作基類 {};
用final修飾的虛函數不可被重寫:
class A { public: A(int data = 1) :_data(data) {} virtual void print() final { cout << _data << endl; } private: int _data; }; class B : public A { void print() //無法重寫“final”函數 "A::print" { //... } };
override關鍵字表示當前函數重寫了基類中的虛函數:
class A { public: A(int data = 1) :_data(data) {} virtual void print() { cout << _data << endl; } private: int _data; }; class B : public A { public: virtual void print() override; }; int main() { B b; b.print(); return 0; }
被override修飾的函數必須重寫基類中該虛函數,不會繼承基類中該函數的實現,以上程序鏈接失敗,因為print函數在派生類中被override修飾,卻沒有具體實現。
5.委派構造函數
委派函數將構造的任務委派給目標構造函數來完成的一種類構造的方式。
class Info { public: //目標構造函數:將構造函數體中重復的代碼提出來作為一個基礎版本,用於在其它構造函數中調用 Info() //被委派 :_a(1) , _c('c') { Init();//一些初始化行為 } //委派構造函數 Info(int a) :Info() //委派 { _a = a; } Info(char c) :Info() //委派 { _c = c; } private: void Init() { //... } private: int _a; char _c; };
構造函數不能同時“委派”和使用參數列表。
6.默認函數控制
在C++中對一個空類編譯器會生成一些默認成員函數,例如構造函數、拷貝構造、賦值運算符重載、析構函數、&和const&的重載、移動構造、移動拷貝構造等。如果顯示的定義了,則不會生成默認的。而有時又需要一個無參的構造函數,C++11可以顯示的控制是否生成默認的函數。
6.1生成默認函數
C++11中在默認函數聲明或定義后加=default,表示讓編譯器生成該函數的默認版本(顯示缺省函數)。
class A { public: A(int a) :_a(a) {} A() = default; //聲明時指定生成默認版本 A& operator=(const A& a); //聲明 private: int _a; }; A& A::operator=(const A& a) = default; //定義時讓編譯器生成默認版本
6.2刪除默認函數
如果想要限制某些默認函數的生成,在C++98中我們會將該函數聲明為私有不實現。在C++11中我們只需在函數聲明后加上=delete即可,編譯器就不會生成該函數。
class A { public: A(int a) :_a(a) {} A() = default; A& operator=(const A& a) = delete; private: int _a; }; int main() { A a(1); A b; b = a; //無法引用 函數 "A::operator=(const A &a)"它是已刪除的函數 return 0; }