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