C++11 的新特性
C++11 的新特性
1 變量和基本類型
1.1 long long 類型
擴展精度浮點數(shù),10位有效數(shù)字
1.2 列表初始化
初始化的幾種不同形式,其中用花括號來初始化變量稱為列表初始化;
比如:
int i = 0; int i = {0}; int i{0}; int i(0);需要注意的是,當用于內(nèi)置類型的變量時,這種初始化形式有一個重要的特點:如果我們使用初始化且初始值存在丟失信息的風險,則編譯器報錯;
例如:
long double ld = 3.1414141414; int a{ld}, b = {ld}; //報錯 int c(ld), d = ld; //正確cout << "a:" << a << "b:" << b << "c:" << c << "d" << d << endl;運行的時候,a,b則會提示報錯信息:error: type 'long double' cannot be narrowed to 'int' in initializer list [-Wc++11-narrowing],這是因為使用long double的值初始化int變量時可能會丟失數(shù)據(jù),所以拒絕a和b的初始化請求;雖然c,d雖然沒有報錯,但是確實丟失了數(shù)據(jù);
[這是為什么?]
1.3 nullptr 常量
有幾種生成空指針的方法:
int *p1 = nullptr; // 等價于int *p1 = 0; int *p2 = 0; int *p3 = NULL; // 等價于int *p3 = 0;在新標準下,建議盡量使用新標準nullptr,nullptr是一種特殊類型的字面值,它可以被轉(zhuǎn)換成任意其它的指針類型,雖然其它的方式也是可以的;
1.4 constexpr 變量
將變量聲明為constexpr類型以便由編譯器來驗證變量的值是否是一個常量表達式;
聲明為constexpr的變量一定是一個常量,而且必須用常量表達式來初始化,比如說下面的情況則是不正確的:
int t = 10; constexpr int q = t + 20; cout << "q" << q << endl;需要將t聲明為?const?才是正確的;
一般來說,如果你認定變量是一個常量表達式,那就把它聲明為constexpr類型;
1.5 類型別名聲明
使用類型別名可以使復雜的類型名字變得更簡單明了,易于理解和使用;
現(xiàn)在有兩種方法可以用來定義類型別名,一種是?typedef?,另一種則是新標準中的?using;
using namespace std;int add(int val) {return 10 + val; }int main() {typedef double dnum;// 字符指針typedef char *pstring;// 函數(shù)// 返回值類型為int,參數(shù)類型為int的函數(shù)typedef int func(int);// 函數(shù)指針,指向返回值類型為int,參數(shù)類型為int的函數(shù)typedef int (*pfunc)(int);// 函數(shù)引用,指向返回值類型為int,參數(shù)類型為int的函數(shù)typedef int (&tfunc)(int);pfunc pfunc_add = nullptr;pfunc_add = add;cout << "函數(shù)指針,result is " << pfunc_add(10) << endl;tfunc tfunc_add = add;cout << "函數(shù)引用,result is " << tfunc_add(10) << endl;func &func_add = add; //這里使用指針或者引用都可以cout << "函數(shù),result is " << func_add(10) << endl;// 數(shù)組// 元素類型為int,個數(shù)為10的數(shù)組typedef int arr[10];// 數(shù)組指針,指向元素類型為int,個數(shù)為10的數(shù)組typedef int (*parr)[10];// 數(shù)組引用,綁定到元素類型為int,個數(shù)為10的數(shù)組typedef int (&tparr)[10];using dnum2 = double;using pstring2 = char*;using func2 = int(int);using pfunc2 = int(*)(int);using arr2 = int[10];using parr2 = int(*)[10];using tparr2 = int(&)[10];std::cout << "Hello, World!" << std::endl;return 0; }但是需要注意的是,如果某個類型別名指代的是復合類型或者常量,那么就會產(chǎn)生意想不到的后果;
比如說
typedef char *pstring; const pstring cstr = 0;按照我們正常的理解就是,將char*替換掉pstring,得到 const char* cstr;然而事實是pstring是一個字符指針,其基本數(shù)據(jù)類型是個指針,此時用此字符指針去聲明cstr,得到的是一個常量的字符指針,但是按照本意是指向char的常量指針,其基本類型是char,也就是說兩者修飾的東西是不一樣的!
但是如果是引用變量則沒有關(guān)系。
參考文章
C++ - 類型別名以及類型的自動推斷
C/C++ 函數(shù)指針的用法
1.6 auto 類型指示符
auto讓編譯器通過初始值來推算變量的類型,所以,其定義的變量必須要有初始值;
使用auto也能在一條語句中聲明多個變量;因為一條聲明語句只能有一個基本數(shù)據(jù)類型,所以該語句中所有的變量的初始基本數(shù)據(jù)類型都必須是一樣的;
- 頂層const:指針本身是一個常量;
- 底層const:指針指向的對象是一個常量;
1.7 decltype 類型指示符
作用:選擇并返回操作數(shù)的數(shù)據(jù)類型;
1.7.1 decltype 和 auto 的區(qū)別
處理頂層const和引用的方式
const int ci = 0, &cj = ci; decltype(ci) x = 0; decltype(cj) y = x; decltype(cj) z; //報錯,因為cj是一個引用,因此作為引用的 z 必須要進行初始化引用從來都是作為其所指對象的同義詞出現(xiàn),也就是根據(jù)其所指的對象決定,只有在decltype處是個例外;
decltype的結(jié)果類型與表達式形式密切相關(guān)
int i = 0; decltype((i)) a; //報錯,因為a類型為 int&,必須進行初始化 decltype(i) b; //正確需要注意的是,decltype((variable))的結(jié)果永遠是引用,而decltype(variable)結(jié)果只有當variable本身就是一個引用時才是引用,其實就是根據(jù)它的類型決定;
1.8 類內(nèi)初始化
建議初始化每一個內(nèi)置類型的變量,為了保證初始化后程序安全
2 字符串、向量和數(shù)組
2.1 使用 auto 或 decltype 縮寫類型
注意的是,如果表達式中已經(jīng)有了size函數(shù)就不要再使用int了,因為size函數(shù)返回的是unsigned,如果其和有符號數(shù)進行比較的時候,有符號數(shù)會自動的轉(zhuǎn)換成一個比較大的無符號數(shù)。
2.2 范圍 for 語句
string str("hello world"); for (auto c : str) {cout << c; }2.3 定義vector對象的vector(向量的向量)
編譯器根據(jù)模版vector生成了三種不同的類型,分別是:
vector<int>,vector<vector<int>>, vector<classname>新的版本已經(jīng)不需要再添加一個空格
vector<vector<int> >2.4 vector對象的列表初始化
放在花括號里
2.5 容器的cbegin 和 cend 函數(shù)
2.6 標準庫begin 和 end 函數(shù)
2.7 使用auto和decltype簡化聲明
3 表達式
3.1 除法的舍入規(guī)則
新標準中,一律向0取整(直接切除小數(shù)部分)
double a = 12/5; cout << a << endl;輸出結(jié)果為2,刪掉了小數(shù)部分;
3.2 用大括號包圍的值列表賦值
3.3 將sizeof用于類成員
使用作用域運算符來獲取類成員的大小是許可的;
- 對數(shù)組執(zhí)行sizeof運算得到整個數(shù)組所占空間的大小,等價于對數(shù)組中
所有的元素各執(zhí)行一次sizeof運算并將所得結(jié)果求和; - 對string對象或vector對象執(zhí)行sizeof運算只返回該類型固定部分的大小,不會計算對象中的元素占用多少空間;
4 語句
4.1 范圍for語句
5 函數(shù)
5.1 標準庫 initializer_list類
可以處理不同數(shù)量實參的函數(shù),前提是實參類型要相同;
其提供的操作包括:
- 默認初始化
- 列表初始化
- 拷貝對象
- size
- begin
- end
5.2 列表初始化返回值
函數(shù)可以返回花括號包圍的值的列表,如果列表為空,臨時量執(zhí)行初始化,否則,返回的值由函數(shù)的返回類型決定;
vector<string> getAddress(int num) {if (num == 0) {return {};}return {"address1", "address2", "address3"}; }5.3 定義尾置返回類型
任何函數(shù)的定義都能夠使用尾置來返回,最適用于返回類型比較復雜的情況;
比如:
// 返回類型為指針,該指針指向含有10個整數(shù)的數(shù)組 auto func(int i) -> int(*)[10] {int arr[10] = {0};return &arr; }5.4 使用decltype簡化返回類型定義
int odd[] = {1, 3, 5, 7, 9}; int even[] = {2, 4, 6, 8, 10};// 返回一個指針,指向數(shù)組 decltype(odd) *getArr(int i) {return (i % 2) ? &odd : &even; }decltype并不會因為獲取的是數(shù)組,則返回的是指針,還是需要在函數(shù)名前面加上?*;
5.5 constexpr函數(shù)
是指能用于常量表達式的函數(shù);
需要遵從幾項約定:
- 函數(shù)的返回類型以及所有形參的類型都是字面值類型(只能用它的值來稱呼它);
- 函數(shù)體中必須有且只有一條return語句(C++14不再做要求);
- 必須非virtual
6 類
6.1 使用=default生成默認構(gòu)造函數(shù)
如果實現(xiàn)了默認的構(gòu)造函數(shù),編譯器則不會自動生成默認版本;可以通過使用關(guān)鍵字?default?來控制默認構(gòu)造函數(shù)的生成,顯示的指示編譯器生成該函數(shù)的默認版本;
class MyClass{ public:MyClass()=default; //同時提供默認版本和帶參版本MyClass(int i):data(i){} private:int data; };比如說如果想要禁止使用拷貝構(gòu)造函數(shù),則使用關(guān)鍵字?delete;
class MyClass { public:MyClass()=default;MyClass(const MyClass& )=delete; };int main() {MyClass my;MyClass my2(my); //報錯return 0; }但需要注意的是,析構(gòu)函數(shù)是不允許使用delete的,否則無法刪除;
6.2 類對象成員的類內(nèi)初始化
當提供一個類內(nèi)初始值時,必須以符號=或者花括號表示
6.3 委托構(gòu)造函數(shù)
一個委托構(gòu)造函數(shù)使用它所屬類的其它構(gòu)造函數(shù)執(zhí)行它自己的初始化過程,或者說它把它自己的一些(或者全部)指責委托給了其它構(gòu)造函數(shù);
class MyClass { public:MyClass() : MyClass(10) {}MyClass(int d) : data(d){}inline int getData() {return data;} private:int data; };int main() {MyClass my;cout << my.getData() << endl;return 0; }6.4 constexpr構(gòu)造函數(shù)
如果想要使得函數(shù)擁有編譯時計算的能力,則使用關(guān)鍵字?constexpr
同樣的編譯時使用對象:
class Square { public:constexpr Square(int e) : edge(e){};constexpr int getArea() {return edge * edge;} private:int edge; };int main() {Square s(10);cout << s.getArea() << endl;return 0; }如果成員函數(shù)標記為?constexpr,則默認其是內(nèi)聯(lián)函數(shù),如果變量聲明為
constexpr,則默認其是?const;
參考文章
constexpr指定符(C++11 起)
7 IO庫
7.1 用string對象處理文件名
新版本中增加了使用庫類型string對象作為文件名,之前只能使用C風格的字符數(shù)組;
ifstream infile("/hello.sh");8 順序容器
補充:
順序容器的類型:
| vector | 可變大小數(shù)組。支持快速隨機訪問。在尾部之外的位置插入或者刪除元素時可能很慢 |
| deque | 雙端隊列。支持快速隨機訪問。在頭尾位置插入/刪除速度很快 |
| list | 雙向鏈表。只支持雙向雙向順序訪問。在list中任何位置進行插入/刪除操作 |
| forward_list | 單向鏈表。只支持單向順序訪問。在鏈表任何位置進行插入/刪除操作都很快 |
| array | 固定大小數(shù)組。支持快速隨即訪問。不能添加或刪除元素 |
| string | 與vector相似的容器,但專門用于保存字符(字符數(shù)組,封裝了char而已)。隨機訪問速度快,在尾部插入、刪除速度快 |
總而言之,數(shù)組隨機訪問速度快,鏈表插入刪除速度快;
8.1 array和forward_list容器
array
int main() {// 使用聚合初始化來構(gòu)造std::array<int, 3> a1{ {1,2,3} }; // C++11中需要使用雙重花括號(而14中不需要)std::array<int, 3> a2 = {1, 2, 3}; // 不再需要等號了std::array<std::string, 2> a3 = { {std::string("a"), "b"} };// 支持基本的容器操作std::sort(a1.begin(), a1.end());std::reverse_copy(a2.begin(), a2.end(), std::ostream_iterator<int>(std::cout, " "));// 支持范圍forfor(auto& s: a3)std::cout << s << ' '; }forward_list
和?list?的使用類似,就不貼代碼了;
template < class T, class Alloc = allocator<T> > class forward_list;Alloc,容器內(nèi)部用來管理內(nèi)存分配以及釋放的內(nèi)存分配器的類型,默認使用的是?std::allocator<T>,;
參考文章
std::array
std::forward_list
8.2 容器的cbegin和cend函數(shù)
返回的是const的迭代器,當不需要寫訪問時,應(yīng)使用cbegin和cend;
8.3 容器的列表初始化
8.4 容器的非成員函數(shù)swap
除了?array?外,swap不對任何元素進行拷貝、刪除或者插入操作,因此可以保證常數(shù)時間內(nèi)完成;swap 只是交換了容器內(nèi)部數(shù)據(jù)結(jié)構(gòu),不會交換元素,因此,除string?外,指向容器的迭代器、引用和指針在 swap 操作后都不會失效;
但是,array會真正的交換它們的元素。
8.5 容器insert成員的返回類型
在新標準下,接受元素個數(shù)或范圍的insert版本返回的是-指向第一個新加入元素的迭代器;
如果范圍為空,不插入任何元素,insert?操作會將第一個參數(shù)返回;
int main() {list<string> lst;auto itr = lst.begin();string word;while (cin >> word) {itr = lst.insert(itr, word); //等價于push_front}return 0; }8.6 容器的emplace成員
當調(diào)用?push?或?insert?成員函數(shù)時,我們將元素類型的對象傳遞給它們,這些對象被拷貝到容器里面,但是,當我們調(diào)用一個?emplace?成員函數(shù)時,則是將函數(shù)傳遞給元素類型的構(gòu)造函數(shù),會使用這些參數(shù)在容器管理的內(nèi)存中直接構(gòu)造元素;包括三個函數(shù):emplace_front,emplace,emplace_back,分別對應(yīng)著?push_front,insert,push_back?為頭部,指定位置,尾部;
class MyClass { public:MyClass(int d) : data(d){}inline int getData() {return data;} private:int data; };int main() {vector<MyClass> mys;mys.emplace_back(10);mys.push_back(MyClass(20));for (auto itr = mys.begin(); itr != mys.end(); itr++) {cout << (*itr).getData() << endl;}return 0; }8.7 shrink_to_fit
調(diào)用該函數(shù)要求?deque,vector,string?退回不需要的內(nèi)存空間;
int main() {vector<int> nums;nums.push_back(10);nums.push_back(10);nums.push_back(10);nums.push_back(10);nums.push_back(10);cout << nums.capacity() << endl;nums.shrink_to_fit();cout << nums.capacity() << endl;return 0; }返回結(jié)果為8,5
區(qū)別:
- reserve:預分配存儲區(qū)大小,即capacity的值
- resize:容器大小,即size的值
8.9 string的數(shù)值轉(zhuǎn)換函數(shù)
新標準中,引入多個函數(shù)實現(xiàn)數(shù)值數(shù)據(jù)和標準庫string之間的轉(zhuǎn)換:
| to_string(val) | 返回任意算術(shù)類型val的字符串 |
| stoi(s, p, b) | int類型 |
| stol(s, p, b) | long類型 |
| stoul(s, p, b) | unsigned long類型 |
| stoll(s, p, b) | long long類型 |
| stoull(s, p, b) | unsigned long long類型 |
| stof(s, p, b) | float類型 |
| stod(s, p, b) | double類型 |
| stold(s, p, b) | long double類型 |
9 泛型算法
9.1 lambda表達式
一個lambda具有一個返回類型、一個參數(shù)列表和一個函數(shù)體;
[capture list](parameter list) -> return type { function body }9.2 lambda表達式中的尾置返回類型
9.3 標準庫bind函數(shù)
auto newCallable = bind(callable, arg_list);例子(綁定類成員函數(shù))
using namespace std; using namespace std::placeholders; class MyClass { public:void fun1(void){cout << "void fun1(void)" << endl;}int fun2(int i){cout << "int fun2(int i)" << " i = " << i << endl;return i;} }; int main() {MyClass my;//使用類對象綁定auto fun1 = bind(&MyClass::fun1, my);fun1();MyClass *p;//使用類指針綁定,-1為占位符auto fun2 = bind(&MyClass::fun2, p, _1);int i = fun2(1);cout << "i = " << i << endl;cin.get();return 0; }10. 關(guān)聯(lián)容器
關(guān)聯(lián)器類型
- map 關(guān)聯(lián)數(shù)組,保存關(guān)鍵字-值對
- set 關(guān)鍵字即值,即只保存關(guān)鍵字的容器
- multimap 關(guān)鍵字可重復出現(xiàn)的map
- multiset 關(guān)鍵字可重復出現(xiàn)的set
- unordered_map 用哈希函數(shù)組織的map
- unordered_set 用哈希函數(shù)組織的set
- unordered_multimap 用哈希函數(shù)組織的map,關(guān)鍵字可重復
- unordered_multiset 用哈希函數(shù)組織的set,關(guān)鍵字可重復
10.1 關(guān)聯(lián)容器的列表初始化
和順序容器差不多
10.2 列表初始化pair的返回類型
10.3 pair的列表初始化
10.4 無序容器
包括4個無序關(guān)聯(lián)容器(使用哈希函數(shù)和關(guān)鍵字類型的 == 運算符)
- unordered_map
- unordered_set
- unordered_multiset
- unordered_multimap
11 動態(tài)內(nèi)存
已總結(jié),略
11.1 智能指針
11.2 shared_ptr類
11.3 動態(tài)分配對象的列表初始化
11.4 auto和動態(tài)分配
11.5 unique_ptr類
11.6 weak_ptr類
11.7 范圍for語句不能應(yīng)用于動態(tài)分配數(shù)組
因為分配的內(nèi)存并不是一個數(shù)組類型
11.8 動態(tài)分配數(shù)組的列表初始化
可以對數(shù)組中的元素進行值初始化,方法是在大小之后跟一對括號;
int *pia = new int[10]; //10個未初始化的int int *pia2 = new int[10](); //10個初始化為0的int int *pia3 = new int[10]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};delete [] pia; //釋放 delete [] pia2; delete [] pia3;11.9 auto不能用于分配數(shù)組
雖然我們用空括號對數(shù)組中元素進行值初始化,但不能在括號中給出初始化器,這意味著不能用auto分配數(shù)組;
11.10 allocator::construct可使用任意構(gòu)造函數(shù)
allocator分配的內(nèi)存是未構(gòu)造的,我們按需要在此內(nèi)存中構(gòu)造對象。在新標準庫中,construct成員函數(shù)接受一個指針和零個或多個額外參數(shù),在給定位置構(gòu)造一個元素。
int main() {allocator<string> alloc;string* str = alloc.allocate(4); //分配10個未初始化的stringauto itr = str;alloc.construct(itr, "chengdu"); //chengducout << str[0] << endl;itr++;alloc.construct(itr, "beijing"); //beijingcout << str[1] << endl;itr++;alloc.construct(itr, 10, 'c'); //10個c組成的字符串cout << str[2] << endl;itr++;alloc.construct(itr); //空字符串cout << str[3] << endl;for (int i = 0; i < 4; i++) {cout << str[i] << endl;}// 銷毀while (itr != str) {alloc.destroy(itr--);}return 0; }為了使用allocate返回的內(nèi)存,我們必須用construct構(gòu)造對象,使用未構(gòu)造的內(nèi)存,其行為是未定義的;我們只有對真正構(gòu)造了的元素進行destroy操作;
12 拷貝控制
12.1 將=default用于拷貝控制類對對象
可以類內(nèi)或者類外修飾成員函數(shù),如果是類內(nèi),合成的函數(shù)將隱式地聲明未內(nèi)聯(lián)的,如果不希望這樣,則在類外聲明;
我們只能對具有合成版本的成員函數(shù)使用=default(即,默認構(gòu)造函數(shù)或拷貝控制成員)
12.2 使用=delete用于拷貝控制成員
對于析構(gòu)函數(shù)已刪除的類型,不能定義該類型的變量或釋放指向該類型動態(tài)分配的指針;
12.3 用移動類對象代替拷貝類對象
為了解決之前存在的性能問題,避免不必要的內(nèi)存拷貝,新標準中有兩種方法來替代拷貝函數(shù):移動函數(shù)和?move
移動構(gòu)造函數(shù)
A(A && h) : a(h.a){h.a = nullptr; }12.4 右值引用
右值引用就是必須綁定到右值的引用,通過?&&?來獲得右值引用;
12.4.1 區(qū)別左值和右值
- 左值:在賦值號左邊,可以被賦值的值,可以取地址;
右值:在賦值號右邊,取出值賦給其他變量的值;
- 左值引用:type & 引用名 = 左值表達式
右值引用:type && 引用名 = 右值表達式
一個對象被用作右值時,使用的是它的內(nèi)容(值),比如1中的?a?,被當作左值時,使用的是它的地址,比如2中的?a,常規(guī)左值;
int main() {int i = 1; //i為常規(guī)左值int &r = i; //正確:r綁定到i上,r是一個引用int &&rr = i; //錯誤:不能將一個右值引用綁定到左值i上int &r2 = i * 1; //錯誤:等號右邊是一個右值,但左值引用只能綁定到左值上int &&rr2 = i * 1; //正確:右值引用綁定到右值上const int &r3 = i * 1; //正確:可以將一個const的左值引用綁定到右值上return 0; }返回左值引用的函數(shù)、賦值、下標、解引用和前置遞增/遞減運算符,都是返回左值的表達式的例子,我們可以將一個左值引用綁定到這類表達式的結(jié)果上;
返回非引用類型的函數(shù),連同算術(shù)、關(guān)系、位以及后置遞增/遞減運算符,都生成右值;我們不能將左值引用綁定到這類表達式上,但是我們可以將一個const的左值引用或者一個右值引用綁定到這類表達式之上;
12.5 標準庫move函數(shù)
我們可以銷毀一個移后源對象,也可以賦予它新值,但不能使用一個移后源對象的值
// 移動構(gòu)造函數(shù),參數(shù) "arg.member" 是左值 A(A&& arg) : member(std::move(arg.member)) { } // 移動賦值函數(shù) A& operator=(A&& other) {member = std::move(other.member);return *this; }12.6 移動構(gòu)造函數(shù)和移動賦值
12.7 移動構(gòu)造函數(shù)通常是noexcept
如果需要通知標準庫這是一個不會拋出異常的移動操作,可以在函數(shù)后面指明?noexcept,如果在一個構(gòu)造函數(shù)中,noexcept?出現(xiàn)在參數(shù)列表和初始化列表開始的冒號之間;
不拋出異常的移動構(gòu)造函數(shù)和移動賦值運算符必須標記為noexcept
12.8 移動迭代器
新標準中可以通過調(diào)用標準庫的?make_move_iterator?函數(shù)將一個普通迭代器轉(zhuǎn)換為一個移動迭代器,這樣可以避免拷貝操作帶來的性能問題;
int main() {std::vector<std::string> foo(3);std::vector<std::string> bar{ "one","two","three" };std::copy(make_move_iterator(bar.begin()),make_move_iterator(bar.end()),foo.begin());// bar now contains unspecified values; clear it:bar.clear();std::cout << "foo:";for (std::string& x : foo) std::cout << ' ' << x;std::cout << '\n';return 0; }12.9 引用限定成員函數(shù)
引用限定符可以是?&?或者?&&,分別指出?this?可以指向一個左值或者右值,引用限定符只能用于成員函數(shù),而且必須同時出現(xiàn)在函數(shù)的聲明和定義中;
可以在參數(shù)列表之后使用引用限定符來指定this對象的左值與右值屬性;
- 若引用限定符為&,則表明this對象指向著左值對象;
- 若引用限定符為&&,則表明this對象指向著右值對象;
參考文章
C++-引用限定符
13 重載運算與類型轉(zhuǎn)換
13.1 function類模版
function模版提供一種通用、多態(tài)的函數(shù)封裝。其實例可以對任何可調(diào)用的目標進行存儲、賦值和調(diào)用操作,這些目標包括函數(shù)、lambda表達式、綁定表達式以及其它函數(shù)對象等;
struct Foo {Foo(int num) : num_(num) {}void print_add(int i) const { std::cout << num_+i << '\n'; }int num_; };void print_num(int i) {std::cout << i << '\n'; }struct PrintNum {void operator()(int i) const{std::cout << i << '\n';} };int main() {// 保存自由函數(shù)std::function<void(int)> f_display = print_num;f_display(-9);// 保存 lambda 表達式std::function<void()> f_display_42 = []() { print_num(42); };f_display_42();// 保存 std::bind 的結(jié)果std::function<void()> f_display_31337 = std::bind(print_num, 31337);f_display_31337();// 保存成員函數(shù)std::function<void(const Foo&, int)> f_add_display = &Foo::print_add;Foo foo(314159);f_add_display(foo, 1);// 保存成員函數(shù)和對象using std::placeholders::_1;std::function<void(int)> f_add_display2= std::bind( &Foo::print_add, foo, _1 );f_add_display2(2);// 保存成員函數(shù)和對象指針std::function<void(int)> f_add_display3= std::bind( &Foo::print_add, &foo, _1 );f_add_display3(3);// 保存函數(shù)對象std::function<void(int)> f_display_obj = PrintNum();f_display_obj(18); }13.2 explicit類型轉(zhuǎn)換運算符
用來防止由構(gòu)造函數(shù)定義的隱式轉(zhuǎn)換;
看看構(gòu)造函數(shù)的隱式轉(zhuǎn)換,如果類的構(gòu)造函數(shù)有一個參數(shù),那么在編譯的時候就會有一個缺省的轉(zhuǎn)換操作,會將其參數(shù)轉(zhuǎn)化成類的對象;
class MyClass { public: MyClass( int num ); } // 編譯器會將10轉(zhuǎn)換成MyClass對象 MyClass obj = 10;避免這種自動轉(zhuǎn)換的操作則需要?explicit:
class MyClass { public: explicit MyClass( int num ); } MyClass obj = 10; //err,can't non-explict convert其只能用在類內(nèi)部的構(gòu)造函數(shù)聲明上,不能用在類外部的函數(shù)定義上;
被聲明為explicit的構(gòu)造函數(shù)通常比其non-explicit兄弟更受歡迎。因為它們禁止編譯器執(zhí)行非預期(往往也不被期望)的類型轉(zhuǎn)換。除非我有一個好理由允許構(gòu)造函數(shù)被用于隱式類型轉(zhuǎn)換,否則我會把它聲明為explicit。---Effective c++
14 面向?qū)ο蟪绦蛟O(shè)計
14.1 虛函數(shù)的override和final指示符
override?可以幫助程序員的意圖更加的清晰的同時讓編譯器可以為我們發(fā)現(xiàn)一些錯誤。其只能用于覆蓋基類的虛函數(shù);
final?使得任何嘗試覆蓋該函數(shù)的操作都將引發(fā)錯誤,并不特指虛函數(shù);
均出現(xiàn)在形參列表(包括任何const或者引用限定符)以及尾置返回類型之后
14.2 刪除的拷貝控制和繼承
如果函數(shù)在基類中被定義為是刪除的,則派生類對應(yīng)的也是刪除的;
14.3 繼承的構(gòu)造函數(shù)
委派和繼承構(gòu)造函數(shù)是由C++11引進為了減少構(gòu)造函數(shù)重復代碼而開發(fā)的兩種不同的特性;
a. 通過特殊的初始化列表語法,委派構(gòu)造函數(shù)允許類的一個構(gòu)造函數(shù)調(diào)用其它的構(gòu)造函數(shù)
X::X(const string& name) : name_(name) {... }X::X() : X("") { }b. 繼承構(gòu)造函數(shù)允許派生類直接調(diào)用基類的構(gòu)造函數(shù),一如繼承基類的其它成員函數(shù),而無需重新聲明,當基類擁有多個構(gòu)造函數(shù)時這一功能尤其有用:
class Base {public:Base();Base(int n);Base(const string& s);... };class Derived : public Base {public:using Base::Base; // Base's constructors are redeclared here. };using聲明語句將令編譯器產(chǎn)生代碼,對于基類的每個構(gòu)造函數(shù),編譯器都在派生類中生成一個形參列表完全相同的構(gòu)造函數(shù);如果派生類含有自己的數(shù)據(jù)成員,則這些成員將會被默認初始化;
15 模版與泛型編程
15.1 聲明模版類型形參為友元
在新標準中,我們可以將模板類型參數(shù)聲明為友元:
template <typename Type> class Bar {friend Type; //將訪問權(quán)限授予用來實例化Bar的類型 };此處我們將用來實例化Bar的類型聲明為友元。因此,對于某個類型名Foo,Foo?將成?Bar<Foo>?的友元。
15.2 模版類型別名
新標準中允許我們?yōu)槟0娑x一個類型別名:
template<typename T> using twin = pair<T, T>; twin<string> authors;其中,twin是一個?pair<T,T>;
15.3 模版函數(shù)的默認模版參數(shù)
template <typename T, typename F = less<T>> int compare(const T &v1, const T &v2, F f= F()) {if (f(v1, v2)) return -1;if (f(v2, v1)) return 1;return 0; }就像之前能為函數(shù)參數(shù)提供默認實參一樣,compare有一個默認模版實參?less<T>和一個默認函數(shù)實參?F()
參考文章
C++類模板與友元的關(guān)系
15.4 實例化的顯示控制
顯示實例化:在不發(fā)生函數(shù)調(diào)用的時候?qū)?span style="margin:0px;padding:0px;">函數(shù)模版實例化或者在不適用類模版的時候?qū)?span style="margin:0px;padding:0px;">類模版實例化,這可以避免實例化相同的模版所帶來的額外開銷;
- 函數(shù)模版實例化
- 類模版實例化
參考文章
C++模板之隱式實例化、顯示實例化、顯示調(diào)用、隱式調(diào)用和模板特化詳解
15.5 模版函數(shù)與尾置返回類型
例子:
//尾置返回允許我們在參數(shù)列表之后聲明返回類型 template <typename T> auto func(T beg, T end) -> decltype(*beg) {return *beg; //返回序列中一個元素的引用 }15.6 引用折疊規(guī)則
規(guī)則如下:
- 所有右值引用折疊到右值引用上仍然是一個右值引用,比如(A&& && 變成 A&&)
- 所有的其它引用類型之間的折疊都將會變成左值引用,比如(A& &變成A&,A& && 變成 A&,A&& & 變成 A&)
引用折疊只能應(yīng)用于間接創(chuàng)建的引用的引用,如類型別名或者模版參數(shù);
15.7 用static_cast將左值轉(zhuǎn)換為右值
可以通過類型轉(zhuǎn)換?static_cast<Type&&>?來將返回右值引用;
int s=101;int&& foo(){return static_cast<int&&>(s); } //返回值為右值引用int main() {int i=foo(); //右值引用作為右值,在賦值運算符的右側(cè)int&& j=foo(); //j是具名引用。因此運算符右側(cè)的右值引用作為左值int* p=&j; //取得j的內(nèi)存地址 }參考文章
右值引用
15.8 標準庫forward函數(shù)
右值引用至少解決了兩個問題:
完美轉(zhuǎn)發(fā)
有的時候,我們需要將一個函數(shù)中某一組參數(shù)原封不動的傳遞給另一個函數(shù),這里不僅僅需要參數(shù)的值不變,而且需要參數(shù)的類型屬性(左值/右值)保持不變-完美轉(zhuǎn)發(fā);
使用forward
- 原型:
- 例子:
但是如果我們沒有使用?forward?函數(shù),結(jié)果則全部都調(diào)用的是 lvalue constructor;
參考文章
Modern C++ | 移動語義與完美轉(zhuǎn)發(fā) | Universal Reference
移動語義(move semantic)和完美轉(zhuǎn)發(fā)(perfect forward)
15.9 可變參數(shù)模版與轉(zhuǎn)發(fā)
右值引用+完美轉(zhuǎn)發(fā)+可變參數(shù)模版實現(xiàn)下面這個函數(shù);
// args為右值引用,decltype用于返回值 template<class Function, class... Args> auto FuncWrapper(Function && f, Args && ... args) -> decltype(f(std::forward<Args>(args)...)) {return f(std::forward<Args>(args)...); }void test0() {cout <<"void"<< endl; }int test1() {return 1; }int test2(int x) {return x; }string test3(string s1, string s2) {return s1 + s2; }int main() {int num = 10;int && nnum = num + 10;int & nnum2 = num;FuncWrapper(test0); // 沒有返回值,打印1cout << FuncWrapper(test1) << endl; // 沒有參數(shù),有返回值,返回1cout << FuncWrapper(test2, 1) << endl; // 有參數(shù),有返回值,返回1cout << FuncWrapper(test2, std::move(num)) << endl; // 有參數(shù),有返回值,返回左值10cout << FuncWrapper(test2, std::move(nnum2)) << endl; // 有參數(shù),有返回值,返回左值引用10cout << FuncWrapper(test2, nnum) << endl; // 有參數(shù),有返回值,返回右值引用20cout << FuncWrapper(test3, "aa", "bb") << endl; // 有參數(shù),有返回值,返回"aabb"return 0; }返回值分別為:
void 1 1 10 10 20 aabb總結(jié)
以上是生活随笔為你收集整理的C++11 的新特性的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: IAR调试按钮功能说明及调试主要看哪些内
- 下一篇: s3c2440移植MQTT