日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > c/c++ >内容正文

c/c++

C++11 特性

發(fā)布時(shí)間:2024/1/8 c/c++ 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++11 特性 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

目錄

C++11簡(jiǎn)介?

{} 初始化

std::initializer_list

auto

decltype

nullptr?

?范圍for循環(huán)

C++98的循環(huán)方式:

C++11的范圍遍歷:

智能指針

1. 為什么需要智能指針?

2.什么是內(nèi)存泄漏?

2.1內(nèi)存泄漏的分類

2.2如何避免內(nèi)存泄漏

3.智能指針的使用及原理

3.1RAII

3.2智能指針的原理

還有常見(jiàn)的三種智能指針類型:

STL中的一些變化

右值引用和移動(dòng)語(yǔ)義?

什么是左值?什么是左值引用呢?

?什么是右值?什么是右值引用?

左值引用和右值引用的比較?

左值引用總結(jié):

?右值引用的總結(jié):

??右值引用的場(chǎng)景和意義:

右值引用左值:

完美轉(zhuǎn)發(fā)

新的類功能?

強(qiáng)制生成的默認(rèn)關(guān)鍵詞default:

?禁止生成默認(rèn)成員函數(shù)的關(guān)鍵詞delete:

可變參數(shù)模板?

用遞歸的方式展開(kāi)參數(shù)包:

逗號(hào)表達(dá)式展開(kāi)參數(shù)包:

?編輯

STL中的emplace接口:?

lambda表達(dá)式

lambda表達(dá)式語(yǔ)法:

?函數(shù)對(duì)象與lambda表達(dá)式:

總結(jié):


C++11簡(jiǎn)介?

在2003年C++標(biāo)準(zhǔn)委員會(huì)曾經(jīng)提交了一份技術(shù)勘誤表(簡(jiǎn)稱TC1),使得C++03這個(gè)名字已經(jīng)取代了
C++98稱為C++11之前的最新C++標(biāo)準(zhǔn)名稱。不過(guò)由于C++03(TC1)主要是對(duì)C++98標(biāo)準(zhǔn)中的漏洞
進(jìn)行修復(fù),語(yǔ)言的核心部分則沒(méi)有改動(dòng),因此人們習(xí)慣性的把兩個(gè)標(biāo)準(zhǔn)合并稱為C++98/03標(biāo)準(zhǔn)。
從C++0x到C++11,C++標(biāo)準(zhǔn)10年磨一劍,第二個(gè)真正意義上的標(biāo)準(zhǔn)珊珊來(lái)遲。相比于
C++98/03,C++11則帶來(lái)了數(shù)量可觀的變化,其中包含了約140個(gè)新特性,以及對(duì)C++03標(biāo)準(zhǔn)中
約600個(gè)缺陷的修正,這使得C++11更像是從C++98/03中孕育出的一種新語(yǔ)言。
相比較而言,
C++11能更好地用于系統(tǒng)開(kāi)發(fā)和庫(kù)開(kāi)發(fā)、語(yǔ)法更加泛華和簡(jiǎn)單化、更加穩(wěn)定和安全,不僅功能更
強(qiáng)大,而且能提升程序員的開(kāi)發(fā)效率,公司實(shí)際項(xiàng)目開(kāi)發(fā)中也用得比較多,所以我們要作為一個(gè)
重點(diǎn)去學(xué)習(xí)。
由于C++11的新增語(yǔ)法特性篇幅特別多,所以文章也講解實(shí)際中比較實(shí)用的語(yǔ)法


{} 初始化

在C++98中,標(biāo)準(zhǔn)允許使用{}對(duì)數(shù)組或者結(jié)構(gòu)體元素進(jìn)行統(tǒng)一的列表初始值設(shè)定,比如:

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中擴(kuò)大了{(lán)}的使用范圍,使其可用于所有的內(nèi)置類型和用戶自定義的類型,使用初始化列表的時(shí)候 = 可加可不加

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表達(dá)式中int* pa = new int[4]{ 0 };//標(biāo)準(zhǔn)容器vector<int> v{1,2,3,4,5};//代替了push_back一個(gè)一個(gè)插入提高代碼效率map<int, int> m{{1,1}, {2,2,},{3,3},{4,4}};return 0; }

創(chuàng)建對(duì)象的時(shí)候也可以使用列表初始化方式調(diào)用構(gòu)造函數(shù)初始化

class Date { public:Date(int year, int month, int day):_year(year),_month(month),_day(day){cout << "Date(int year, int month, int day)" << endl;} private:int _year;int _month;int _day; }; int main() {Date d1(2022, 1, 1); // old style// C++11支持的列表初始化,這里會(huì)調(diào)用構(gòu)造函數(shù)初始化Date d2{ 2022, 1, 2 };Date d3 = { 2022, 1, 3 };return 0; }

std::initializer_list

?initializer? 的使用:

int main() {vector<int> v = { 1,2,3,4 };list<int> lt = { 1,2 };// 這里{"sort", "排序"}會(huì)先初始化構(gòu)造一個(gè)pair對(duì)象map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"} };// 使用大括號(hào)對(duì)容器賦值v = {10, 20, 30};return 0; }

讓模擬實(shí)現(xiàn)的vector也可以實(shí)現(xiàn)用{}賦值

template<class T> class vector { public:typedef T* iterator;vector(initializer_list<T> l){_start = new T[l.size()];_finish = _start + l.size();_endofstorage = _start + l.size();iterator vit = _start;typename initializer_list<T>::iterator lit = l.begin();while (lit != l.end()){*vit++ = *lit++;}}vector<T>& operator=(initializer_list<T> l) {vector<T> tmp(l);std::swap(_start, tmp._start);std::swap(_finish, tmp._finish);std::swap(_endofstorage, tmp._endofstorage);return *this;} private:iterator _start;iterator _finish;iterator _endofstorage; };

auto

在C++98中auto是一個(gè)存儲(chǔ)類型的說(shuō)明符,表明變量是局部自動(dòng)存儲(chǔ)類型,但是局部域中定義局
部的變量默認(rèn)就是自動(dòng)存儲(chǔ)類型,所以auto就沒(méi)什么價(jià)值了。C++11中廢棄auto原來(lái)的用法,將
其用于實(shí)現(xiàn)自動(dòng)類型推斷。這樣要求必須進(jìn)行顯示初始化,讓編譯器將定義對(duì)象的類型設(shè)置為初
始化值的類型。

?

int main() {int i = 10;auto p = &i;auto pf = strcpy;cout << typeid(p).name() << endl; cout << typeid(pf).name() << endl;map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"} };auto it = dict.begin();cout<<typeid(it).name()<<endl;return 0; }

decltype

作用:將變量的聲明類型作為表達(dá)式指定的類型?

使用場(chǎng)景:

template<class T1, class T2> void F(T1 t1, T2 t2) {decltype(t1 * t2) ret;cout << typeid(ret).name() << endl; } int main() {const int x = 1;double y = 2.2;decltype(x * y) ret; // ret的類型是doubledecltype(&x) p; // p的類型是int*cout << typeid(ret).name() << endl;cout << typeid(p).name() << endl;F(1, 'a');return 0; }

nullptr?

由于C++中NULL被定義成字面量0,這樣就可能回帶來(lái)一些問(wèn)題,因?yàn)?既能指針常量,又能表示
整形常量。所以出于清晰和安全的角度考慮,C++11中新增了nullptr,用于表示空指針
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
?


?范圍for循環(huán)

C++98的循環(huán)方式:

int arr[] = { 3,4,2,1,5,6};// 下標(biāo)遍歷for(int i = 0; i < sizeof(arr) / sizeof(arr[0]); ++i){std::cout << arr[i] << " ";}// 指針遍歷for(int* p = arr; p < arr + sizeof(arr) / sizeof(arr[0]); ++p){std::cout << *p << " ";}

C++11的范圍遍歷:

C++98的for循環(huán)遍歷的特點(diǎn)就是要寫(xiě)明一個(gè)明確的范圍,如果我們需要完整的遍歷一個(gè)數(shù)組或者集合或者容器(map)的時(shí)候,可以讓編譯器來(lái)自動(dòng)推算范圍。這就引出C++11的范圍for。

int arr[] = { 3,4,2,1,5,6};// 下標(biāo)遍歷for(int &e : arr){cout << e <<" ";}//使用auto的話會(huì)更簡(jiǎn)單for(auto &e : arr){cout << e <<" ";}//不明確的區(qū)間不能范圍forint *p = arr;for(auto &e : p)//范圍不確定,運(yùn)行報(bào)錯(cuò){cout << e <<" ";}

但是范圍遍歷并不適用于所有情況,范圍遍歷的前提條件就是迭代的區(qū)間范圍都是確定的。比如一般的容器vector,map,set,string,list.用戶自己實(shí)現(xiàn)的類要想實(shí)現(xiàn)的話需要自行提供自增運(yùn)算符重載和賦值運(yùn)算符重載?

智能指針

1. 為什么需要智能指針?

int div() {int a, b;cin >> a >> b;if (b == 0)throw invalid_argument("除0錯(cuò)誤");return a / b; } void Func() {int* p1 = new int; //當(dāng)p1和p2被正常的new出來(lái)int* p2 = new int;cout << div() << endl;//調(diào)用div時(shí)候如果輸入的b為0,那么根據(jù)異常的特性會(huì)在這里直接返回//這樣直接跳過(guò)下面的delete了,導(dǎo)致了內(nèi)存泄漏 delete p1;delete p2; } int main() {try{Func();//這里捕捉異常}catch (exception& e){cout << e.what() << endl;}return 0; }

分析上述代碼。

2.什么是內(nèi)存泄漏?

內(nèi)存泄漏指的是,人為的疏忽導(dǎo)致程序未能釋放已經(jīng)不再使用的內(nèi)存的情況。內(nèi)存泄漏指的并不是內(nèi)存在物理上的消失,而是程序員分配某段內(nèi)存后,失去了對(duì)該段內(nèi)存的控制,造成內(nèi)存浪費(fèi)

內(nèi)存泄漏的危害:長(zhǎng)期運(yùn)行的程序出現(xiàn)內(nèi)存泄漏,影響很大,如操作系統(tǒng)、后臺(tái)服務(wù)等等,出現(xiàn)
內(nèi)存泄漏會(huì)導(dǎo)致響應(yīng)越來(lái)越慢,最終卡死。

2.1內(nèi)存泄漏的分類

  • 堆內(nèi)存泄露*(Heap?leak)

????????堆內(nèi)存指的是程序執(zhí)行中依據(jù)須要分配通過(guò)malloc / calloc / realloc / new等從堆中分配的一
????????塊內(nèi)存,用完后必須通過(guò)調(diào)用相應(yīng)的 free或者delete 刪掉。假設(shè)程序的設(shè)計(jì)錯(cuò)誤導(dǎo)致這部分
????????內(nèi)存沒(méi)有被釋放,那么以后這部分空間將無(wú)法再被使用,就會(huì)產(chǎn)生Heap Leak。

  • 系統(tǒng)資源泄露

????????指程序使用系統(tǒng)分配的資源,比方套接字、文件描述符、管道等沒(méi)有使用對(duì)應(yīng)的函數(shù)釋放
????????掉,導(dǎo)致系統(tǒng)資源的浪費(fèi),嚴(yán)重可導(dǎo)致系統(tǒng)效能減少,系統(tǒng)執(zhí)行不穩(wěn)定

2.2如何避免內(nèi)存泄漏

1. 工程前期良好的設(shè)計(jì)規(guī)范,養(yǎng)成良好的編碼規(guī)范,申請(qǐng)的內(nèi)存空間記著匹配的去釋放。ps:
這個(gè)理想狀態(tài)。但是如果碰上異常時(shí),就算注意釋放了,還是可能會(huì)出問(wèn)題。需要下一條智
能指針來(lái)管理才有保證。
2. 采用RAII思想或者智能指針來(lái)管理資源
3. 有些公司內(nèi)部規(guī)范使用內(nèi)部實(shí)現(xiàn)的私有內(nèi)存管理庫(kù)。這套庫(kù)自帶內(nèi)存泄漏檢測(cè)的功能選項(xiàng)。
4. 出問(wèn)題了使用內(nèi)存泄漏工具檢測(cè)。ps:不過(guò)很多工具都不夠靠譜,或者收費(fèi)昂貴。


內(nèi)存泄漏非常常見(jiàn),解決方案分為兩種:1、事前預(yù)防型。如智能指針等。2、事后查錯(cuò)型。如泄漏檢測(cè)工具
?

3.智能指針的使用及原理

3.1RAII

RAII(Resource Acquisition Is Initialization)是一種利用對(duì)象生命周期來(lái)控制程序資源(如內(nèi)
存、文件句柄、網(wǎng)絡(luò)連接、互斥量等等)的簡(jiǎn)單技術(shù)
在對(duì)象構(gòu)造時(shí)獲取資源,接著控制對(duì)資源的訪問(wèn)使之在對(duì)象的生命周期內(nèi)始終保持有效,最后在
對(duì)象析構(gòu)的時(shí)候釋放資源
。借此,我們實(shí)際上把管理一份資源的責(zé)任托管給了一個(gè)對(duì)象

優(yōu)點(diǎn):1.不需要顯示的釋放資源 2.這種方式同時(shí)可以保證對(duì)象所需的資源在其生命周期內(nèi)始終有效

// 使用RAII思想設(shè)計(jì)的SmartPtr類 template<class T> class SmartPtr { public:SmartPtr(T* ptr = nullptr): _ptr(ptr){}~SmartPtr(){if(_ptr)delete _ptr;} private:T* _ptr; };

3.2智能指針的原理

上述的SmartPtr還不能稱作智能指針,因?yàn)樗€不具備指針的行為。指針可以解引用,可以通過(guò)->訪問(wèn)他的成員。因此AutoPtr模板中還需要重載 * ,->

template<class T> class SmartPtr { public:SmartPtr(T* ptr = nullptr): _ptr(ptr){}~SmartPtr(){if(_ptr)delete _ptr;}T& operator*() {return *_ptr;}T* operator->() {return _ptr;} private:T* _ptr; }; struct Date {int _year;int _month;int _day; }; int main() {SmartPtr<int> sp1(new int);*sp1 = 10cout<<*sp1<<endl;SmartPtr<int> sparray(new Date);// 需要注意的是這里應(yīng)該是sparray.operator->()->_year = 2018;// 本來(lái)應(yīng)該是sparray->->_year這里語(yǔ)法上為了可讀性,省略了一個(gè)->sparray->_year = 2018;sparray->_month = 1;sparray->_day = 1; }

總結(jié)一下:智能指針的原理 1.RAII 2.將類重載*,->使其具備指針的行為?

還有常見(jiàn)的三種智能指針類型:

  • std::auto_ptr
  • std::unique_ptr
  • std::shared_ptr

  • STL中的一些變化

    ?實(shí)際中最有用的就是:unordered_map和unordered_set在上片博客講了unordered_map和map的區(qū)別。https://blog.csdn.net/Obto_/article/details/128388643?spm=1001.2014.3001.5502

    實(shí)際上更新了C++11后,容器中增加的新方法->插入接口函數(shù)的右值版本,都說(shuō)這樣可以增加效率,具體的看下文對(duì)右值的詳細(xì)講述。


    右值引用和移動(dòng)語(yǔ)義?

    傳統(tǒng)的C++語(yǔ)法中就有引用的語(yǔ)法,而C++11中新增了的右值引用語(yǔ)法特性,所以從現(xiàn)在開(kāi)始我們
    之前學(xué)習(xí)的引用就叫做左值引用。無(wú)論左值引用還是右值引用,都是給對(duì)象取別名。

    什么是左值?什么是左值引用呢?

    左值是一個(gè)數(shù)據(jù)的表達(dá)式,我們可以取他的地址,可以對(duì)他賦值,左值可以出現(xiàn)在賦值符號(hào)的左邊,而右值不能出現(xiàn)在賦值符號(hào)的左邊。定義const的左值就不能給他賦值,但可以對(duì)他取地址。

    左值引用就是給左值取別名。?

    int main() {// 以下的p、b、c、*p都是左值int* p = new int(0);int b = 1;const int c = 2;// 以下幾個(gè)是對(duì)上面左值的左值引用int*& rp = p;int& rb = b;const int& rc = c;int& rp = *p;return 0; }

    ?什么是右值?什么是右值引用?

    右值也是一個(gè)表達(dá)數(shù)據(jù)的表達(dá)式,比如:字面常量,表達(dá)式返回值,函數(shù)返回值(這個(gè)不能是左值引用的返回值)等等,右值可以出現(xiàn)在賦值符號(hào)的右邊,但是不能出現(xiàn)出現(xiàn)在賦值符號(hào)的左邊,右值不能取地址。右值引用就是對(duì)右值的引用,給右值取別名。

    int main() {double x = 1.1, y = 2.2;// 以下幾個(gè)都是常見(jiàn)的右值10;x + y;fmin(x, y);// 以下幾個(gè)都是對(duì)右值的右值引用int&& rr1 = 10;double&& rr2 = x + y;double&& rr3 = fmin(x, y);// 這里編譯會(huì)報(bào)錯(cuò):error C2106: “=”: 左操作數(shù)必須為左值10 = 1;x + y = 1;fmin(x, y) = 1;return 0; }

    需要注意的是:右值是不能取地址,但可以取別名,當(dāng)給右值取別名后會(huì)導(dǎo)致右值被存儲(chǔ)到特定的位置。可以對(duì)右值的別名進(jìn)行左值的操作。


    ?

    左值引用和右值引用的比較?

    左值引用總結(jié):

    1.左值引用只能引用左值,不能引用右值?

    2.但是const左值既可以引用左值,也可以引用右值

    int main() {// 左值引用只能引用左值,不能引用右值。int a = 10;int& ra1 = a; // ra為a的別名//int& ra2 = 10; // 編譯失敗,因?yàn)?0是右值// const左值引用既可引用左值,也可引用右值。const int& ra3 = 10;const int& ra4 = a;return 0;}

    ?右值引用的總結(jié):

    1.右值引用只能引用右值,不能引用左值

    2.但是右值引用可以用move后的左值

    int main() {// 右值引用只能右值,不能引用左值。int&& r1 = 10;// error C2440: “初始化”: 無(wú)法從“int”轉(zhuǎn)換為“int &&”// message : 無(wú)法將左值綁定到右值引用int a = 10;int&& r2 = a;// 右值引用可以引用move以后的左值int&& r3 = std::move(a);return 0; }

    ??右值引用的場(chǎng)景和意義:

    #include<assert.h> namespace gch {class string{public:typedef char* iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}string(const char* str = ""):_size(strlen(str)), _capacity(_size){//cout << "string(char* str)" << endl;_str = new char[_capacity + 1];strcpy(_str, str);}// s1.swap(s2)void swap(string& s){::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);}// 拷貝構(gòu)造string(const string& s):_str(nullptr){cout << "string(const string& s) -- 深拷貝" << endl;string tmp(s._str);swap(tmp);}// 賦值重載string& operator=(const string& s){cout << "string& operator=(string s) -- 深拷貝" << endl;string tmp(s);swap(tmp);return *this;}// 移動(dòng)構(gòu)造string(string&& s):_str(nullptr), _size(0), _capacity(0){cout << "string(string&& s) -- 移動(dòng)語(yǔ)義" << endl;swap(s);}// 移動(dòng)賦值string& operator=(string&& s){cout << "string& operator=(string&& s) -- 移動(dòng)語(yǔ)義" << endl;swap(s);return *this;}~string(){delete[] _str;_str = nullptr;}char& operator[](size_t pos){assert(pos < _size);return _str[pos];}void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void push_back(char ch){if (_size >= _capacity){size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}_str[_size] = ch;++_size;_str[_size] = '\0';}//string operator+=(char ch)string& operator+=(char ch){push_back(ch);return *this;}const char* c_str() const{return _str;}private:char* _str;size_t _size;size_t _capacity; // 不包含最后做標(biāo)識(shí)的\0}; }int main() {gch::string s1("ai");return 0; }

    左值引用的使用場(chǎng)景:

    void func1(bit::string s) {} void func2(const bit::string& s) {} int main() {bit::string s1("hello world");// func1和func2的調(diào)用我們可以看到左值引用做參數(shù)減少了拷貝,提高效率的使用場(chǎng)景和價(jià)值func1(s1);func2(s1);//但左值引用能做的不夠全面,比如傳值返回的時(shí)候就會(huì)存在深拷貝// string operator+=(char ch) 傳值返回存在深拷貝// string& operator+=(char ch) 傳左值引用沒(méi)有拷貝提高了效率s1 += '!';return 0; }

    左值引用的短板:? 當(dāng)函數(shù)返回對(duì)象是一個(gè)局部變量,出了函數(shù)作用域就不存在了,就不能使用左值引用進(jìn)行返回,只能傳值返回。例如:gch::string to_string(int value)函數(shù)中可以看到,這里只能傳值返回,傳值返回會(huì)導(dǎo)致至少一次的深拷貝(舊的編譯器如果沒(méi)有優(yōu)化的話可能會(huì)有兩次深拷貝)

    ?

    右值引用和移動(dòng)語(yǔ)義解決上述的問(wèn)題:

    在gch::string中新增了移動(dòng)構(gòu)造,移動(dòng)構(gòu)造本質(zhì)使用其他人的資源來(lái)構(gòu)造自己,這樣就不用深拷貝了。

    string(string&& s):_str(nullptr),_size(0),_capacity(0) {cout << "string(string&& s) -- 移動(dòng)語(yǔ)義" << endl;swap(s); } int main() {bit::string ret2 = bit::to_string(-1234);return 0; }

    在移動(dòng)構(gòu)造中沒(méi)有開(kāi)空間,沒(méi)拷貝數(shù)據(jù),所以效率提高了

    下列是移動(dòng)賦值:

    //移動(dòng)賦值 string& operator=(string&& s) {cout << "string& operator=(string&& s) -- 移動(dòng)語(yǔ)義" << endl;swap(s);return *this; } int main() {bit::string ret1;ret1 = bit::to_string(1234);return 0; }

    ::STL容器都增加了移動(dòng)拷貝和移動(dòng)賦值


    右值引用左值:

    按照語(yǔ)法,右值引用只能引用右值,但右值引用一定不能引用左值的嗎?答案顯然是否定的,在很多場(chǎng)景下需要右值引用去引用左值實(shí)現(xiàn)移動(dòng)語(yǔ)義,當(dāng)需要右值引用一個(gè)左值的時(shí)候,可以使用move函數(shù),move(左值)這樣可以將左值強(qiáng)轉(zhuǎn)成右值,就可以進(jìn)行對(duì)左值的移動(dòng)了

    int main() {gch::string s1("hello world");// 這里s1是左值,調(diào)用的是拷貝構(gòu)造gch::string s2(s1);// 這里我們把s1 move處理以后, 會(huì)被當(dāng)成右值,調(diào)用移動(dòng)構(gòu)造// 但是這里要注意,一般是不要這樣用的,因?yàn)槲覀儠?huì)發(fā)現(xiàn)s1的// 資源被轉(zhuǎn)移給了s3,s1被置空了。gch::string s3(std::move(s2));return 0; }

    完美轉(zhuǎn)發(fā)

    模板中的&&萬(wàn)能引用?

    void Fun(int &x){ cout << "左值引用" << endl; } void Fun(const int &x){ cout << "const 左值引用" << endl; } void Fun(int &&x){ cout << "右值引用" << endl; } void Fun(const int &&x){ cout << "const 右值引用" << endl; }// 模板中的&&不代表右值引用,而是萬(wàn)能引用,其既能接收左值又能接收右值。 // 模板的萬(wàn)能引用只是提供了能夠接收同時(shí)接收左值引用和右值引用的能力, // 但是引用類型的唯一作用就是限制了接收的類型,后續(xù)使用中都退化成了左值, // 我們希望能夠在傳遞過(guò)程中保持它的左值或者右值的屬性, 就需要用我們下面學(xué)習(xí)的完美轉(zhuǎn)發(fā)template<typename T> void PerfectForward(T&& t) {Fun(t); } int main() {PerfectForward(10); // 右值int a;PerfectForward(a); // 左值PerfectForward(std::move(a)); // 右值const int b = 8;PerfectForward(b); // const 左值PerfectForward(std::move(b)); // const 右值return 0; }

    運(yùn)行結(jié)果:

    ? ? ? ? ? ?完美轉(zhuǎn)發(fā)會(huì)在傳參的過(guò)程中對(duì)象的原生類型屬性?


    新的類功能?

    ?原來(lái)C++的類中,有6個(gè)默認(rèn)成員函數(shù)。C++11后新增兩個(gè)移動(dòng)構(gòu)造函數(shù)和移動(dòng)賦值運(yùn)算符重載。

    針對(duì)移動(dòng)構(gòu)造函數(shù)和移動(dòng)賦值運(yùn)算符重載有一些需要注意的點(diǎn)如下
    ?

    • 如果你沒(méi)有自己實(shí)現(xiàn)移動(dòng)構(gòu)造函數(shù),且沒(méi)有實(shí)現(xiàn)析構(gòu)函數(shù) 、拷貝構(gòu)造、拷貝賦值重載中的任
      意一個(gè)。那么編譯器會(huì)自動(dòng)生成一個(gè)默認(rèn)移動(dòng)構(gòu)造。默認(rèn)生成的移動(dòng)構(gòu)造函數(shù),對(duì)于內(nèi)置類
      型成員會(huì)執(zhí)行逐成員按字節(jié)拷貝,自定義類型成員,則需要看這個(gè)成員是否實(shí)現(xiàn)移動(dòng)構(gòu)造,
      如果實(shí)現(xiàn)了就調(diào)用移動(dòng)構(gòu)造,沒(méi)有實(shí)現(xiàn)就調(diào)用拷貝構(gòu)造。
      ?

    • 如果你沒(méi)有自己實(shí)現(xiàn)移動(dòng)賦值重載函數(shù),且沒(méi)有實(shí)現(xiàn)析構(gòu)函數(shù) 、拷貝構(gòu)造、拷貝賦值重載中
      的任意一個(gè),那么編譯器會(huì)自動(dòng)生成一個(gè)默認(rèn)移動(dòng)賦值。默認(rèn)生成的移動(dòng)構(gòu)造函數(shù),對(duì)于內(nèi)
      置類型成員會(huì)執(zhí)行逐成員按字節(jié)拷貝,自定義類型成員,則需要看這個(gè)成員是否實(shí)現(xiàn)移動(dòng)賦
      值,如果實(shí)現(xiàn)了就調(diào)用移動(dòng)賦值,沒(méi)有實(shí)現(xiàn)就調(diào)用拷貝賦值。(默認(rèn)移動(dòng)賦值跟上面移動(dòng)構(gòu)造
      完全類似)
      ?
    • 如果你提供了移動(dòng)構(gòu)造或者移動(dòng)賦值,編譯器不會(huì)自動(dòng)提供拷貝構(gòu)造和拷貝賦值。這里要注意:一定要配套的全部實(shí)現(xiàn)不要單獨(dú)實(shí)現(xiàn)移動(dòng)構(gòu)造或者移動(dòng)賦值

    強(qiáng)制生成的默認(rèn)關(guān)鍵詞default:

    C++11為了讓你更好的控制函數(shù),假設(shè)有一個(gè)你需要用到的默認(rèn)成員函數(shù),因?yàn)槟骋恍┰驔](méi)有生成,那就可以用default來(lái)強(qiáng)制生成默認(rèn)成員函數(shù)。比如:我們提供了拷貝構(gòu)造就不會(huì)再生成移動(dòng)構(gòu)造了,可以加個(gè)default來(lái)強(qiáng)制它生成

    class Person { public:Person(const char* name = "", int age = 0):_name(name), _age(age){}Person(const Person& p):_name(p._name),_age(p._age){}Person(Person&& p) = default; private:bit::string _name;int _age; }; int main() {Person s1;Person s2 = s1;Person s3 = std::move(s1);return 0; }

    ?禁止生成默認(rèn)成員函數(shù)的關(guān)鍵詞delete:

    如果能想要限制某些默認(rèn)函數(shù)的生成,在C++98中,是該函數(shù)設(shè)置成private,并且只聲明補(bǔ)丁
    已,這樣只要其他人想要調(diào)用就會(huì)報(bào)錯(cuò)。在C++11中更簡(jiǎn)單,只需在該函數(shù)聲明加上=delete即
    可,該語(yǔ)法指示編譯器不生成對(duì)應(yīng)函數(shù)的默認(rèn)版本,稱=delete修飾的函數(shù)為刪除函數(shù)。

    class Person { public:Person(const char* name = "", int age = 0):_name(name), _age(age){} Person(const Person& p) = delete; private:bit::string _name;int _age; }; int main() {Person s1;Person s2 = s1;Person s3 = std::move(s1);return 0; }

    可變參數(shù)模板?

    template <class ...Args> void ShowList(Args... args) {}

    ?Args是一個(gè)模板參數(shù)包,args是一個(gè)函數(shù)形參參數(shù)包,聲明一個(gè)參數(shù)包Args...args,這個(gè)參數(shù)包可以包含0到任意個(gè)模板參數(shù)。

    上面的參數(shù)args前面有省略號(hào),所以它就是一個(gè)可變模版參數(shù),我們把帶省略號(hào)的參數(shù)稱為“參數(shù)包”,它里面包含了0到N(N>=0)個(gè)模版參數(shù)。我們無(wú)法直接獲取參數(shù)包args中的每個(gè)參數(shù)的,只能通過(guò)展開(kāi)參數(shù)包的方式來(lái)獲取參數(shù)包中的每個(gè)參數(shù),這是使用可變模版參數(shù)的一個(gè)主要特點(diǎn),也是最大的難點(diǎn),即如何展開(kāi)可變模版參數(shù)。由于語(yǔ)法不支持使用args[i]這樣方式獲取可變參數(shù),所以我們的用一些奇招來(lái)一一獲取參數(shù)包的值。

    用遞歸的方式展開(kāi)參數(shù)包:

    // 遞歸終止函數(shù) template <class T> void ShowList(const T& t) {cout << t << endl; } // 展開(kāi)函數(shù) template <class T, class ...Args> void ShowList(T value, Args... args) {cout << value <<" ";ShowList(args...); } int main() {ShowList(1);ShowList(1, 'A');ShowList(1, 'A', std::string("sort"));return 0; }

    逗號(hào)表達(dá)式展開(kāi)參數(shù)包:

    這種展開(kāi)參數(shù)包的方式,不需要通過(guò)遞歸終止函數(shù),是直接在expand函數(shù)體中展開(kāi)的, printarg
    不是一個(gè)遞歸終止函數(shù),只是一個(gè)處理參數(shù)包中每一個(gè)參數(shù)的函數(shù)。

    這種就地展開(kāi)參數(shù)包的方式實(shí)現(xiàn)的關(guān)鍵是逗號(hào)表達(dá)式。我們知道逗號(hào)表達(dá)式會(huì)按順序執(zhí)行逗號(hào)前面的表達(dá)式。
    expand函數(shù)中的逗號(hào)表達(dá)式:(printarg(args), 0),也是按照這個(gè)執(zhí)行順序,先執(zhí)行printarg(args),再得到逗號(hào)表達(dá)式的結(jié)果0。同時(shí)還用到了C++11的另外一個(gè)特性——初始化列表,通過(guò)初始化列表來(lái)初始化一個(gè)變長(zhǎng)數(shù)組,

    {(printarg(args), 0)...}將會(huì)展開(kāi)成((printarg(arg1),0),(printarg(arg2),0), (printarg(arg3),0), etc... ),最終會(huì)創(chuàng)建一個(gè)元素值都為0的數(shù)組int arr[sizeof...(Args)]。由于是逗號(hào)表達(dá)式,在創(chuàng)建數(shù)組的過(guò)程中會(huì)先執(zhí)行逗號(hào)表達(dá)式前面的部分printarg(args)打印出參數(shù),也就是說(shuō)在構(gòu)造int數(shù)組的過(guò)程中就將參數(shù)包展開(kāi)了,這個(gè)數(shù)組的目的純粹是為了在數(shù)組構(gòu)造的過(guò)程展開(kāi)參數(shù)包

    template <class T> void PrintArg(T t) {cout << t << " "; } //展開(kāi)函數(shù) template <class ...Args> void ShowList(Args... args) {int arr[] = { (PrintArg(args), 0)... };cout << endl; } int main() {ShowList(1);ShowList(1, 'A');ShowList(1, 'A', std::string("sort"));return 0; }


    ?

    STL中的emplace接口:

    emplace接口支持模板的可變參數(shù),并且萬(wàn)能引用。相對(duì)于insert他的優(yōu)勢(shì)在哪里呢??

    int main() {std::list< std::pair<int, char> > mylist;// emplace_back支持可變參數(shù),拿到構(gòu)建pair對(duì)象的參數(shù)后自己去創(chuàng)建對(duì)象// 那么在這里我們可以看到除了用法上,和push_back沒(méi)什么太大的區(qū)別mylist.emplace_back(10, 'a');mylist.emplace_back(20, 'b');mylist.emplace_back(make_pair(30, 'c'));mylist.push_back(make_pair(40, 'd'));mylist.push_back({ 50, 'e' });for (auto e : mylist)cout << e.first << ":" << e.second << endl;return 0; }

    emplace_back支持可變參數(shù),拿到pair對(duì)象的參數(shù)后自己去創(chuàng)建對(duì)象。和push_back沒(méi)有太大區(qū)別

    區(qū)別就是,emplace_back是直接構(gòu)造對(duì)象出來(lái)在list中,push_back是先構(gòu)造一個(gè)對(duì)象然后再移動(dòng)構(gòu)造,效率對(duì)于今天來(lái)說(shuō)影響并不是很大。


    lambda表達(dá)式

    在C++98中想對(duì)一個(gè)數(shù)據(jù)進(jìn)行排序,一般使用std::sort??

    int main() {int array[] = {3,1,2,4,6,8,5,9};// 默認(rèn)是升序std::sort(array, array+sizeof(array)/sizeof(array[0]));默認(rèn)是less<int>()// 如果需要降序,需要改變?cè)氐谋容^規(guī)則std::sort(array, array + sizeof(array) / sizeof(array[0]), greater<int>());return 0; }

    而lambda表達(dá)式的?出現(xiàn)給程序員提供了很大的便攜;

    struct Goods {string _name; // 名字double _price; // 價(jià)格int _evaluate; // 評(píng)價(jià) Goods(const char* str, double price, int evaluate):_name(str), _price(price), _evaluate(evaluate){} };int main() {vector<Goods> v = { { "蘋(píng)果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,3 }, { "菠蘿", 1.5, 4 } };sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2){return g1._price < g2._price; });sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2){return g1._price > g2._price; });sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2){return g1._evaluate < g2._evaluate; });sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2){return g1._evaluate > g2._evaluate; }); }

    看的出來(lái)其實(shí)lamda表達(dá)式就是一個(gè)匿名函數(shù)。

    lambda表達(dá)式語(yǔ)法:
    ?

    lambda表達(dá)式書(shū)寫(xiě)格式:[capture-list] (parameters) mutable -> return-type { statement }


    [ capture_list ] :?捕捉列表,出現(xiàn)在lambda表達(dá)式開(kāi)頭,編譯器根據(jù)[]來(lái)判斷接下來(lái)的代碼是否為lambda函數(shù),捕捉列表能夠捕捉上下文中變量供lambda函數(shù)使用。?

    (parameters) :?參數(shù)列表。與普通函數(shù)的參數(shù)列表一致,如果不需要傳遞參數(shù)可以連()一起省了

    mutable :?默認(rèn)情況下,lambda函數(shù)總是一個(gè)const函數(shù),mutable可以取消他的常性。使用該修飾符時(shí),參數(shù)列表不能省略(即使參數(shù)為空)?

    return -type :?返回值類型。用追蹤返回值類型形式聲明函數(shù)的返回值類型,沒(méi)有返回值可省略,有返回值也可以省略讓編譯器自動(dòng)推導(dǎo)

    {statement} :?函數(shù)體。在該函數(shù)體內(nèi),可以使用其參數(shù)外,還可以使用所有捕捉到的變量。

    注意:lambda函數(shù)定義中,參數(shù)列表和返回值類型都是可選部分,而捕捉列表可以為空。所以C++11中最簡(jiǎn)單的lambda表達(dá)式為 []{}

    ?函數(shù)對(duì)象與lambda表達(dá)式:

    lass Rate { public:Rate(double rate): _rate(rate){}double operator()(double money, int year){ return money * _rate * year;} private:double _rate; }; int main() {// 函數(shù)對(duì)象double rate = 0.49;Rate r1(rate);r1(10000, 2);// lamberauto r2 = [=](double monty, int year)->double{return monty*rate*year;};r2(10000, 2);return 0; }

    從使用方式上看,函數(shù)對(duì)象與lambda表達(dá)式完全一樣

    實(shí)際在底層編譯器對(duì)于lambda表達(dá)式的處理方式,完全就是按照函數(shù)對(duì)象的方式處理的,如果定義了一個(gè)lambda表達(dá)式,編譯器會(huì)自動(dòng)生成一個(gè)類,在該類中重載了operator()

    總結(jié):

    ?C++11的更新的特性非常多,本篇博客講述了C++11中使用較多的新語(yǔ)法。

    1.{}更方便了類的初始化,使自定義類型的類更方便的初始化

    2.auto廢棄了原先的用法,取而代之的是自動(dòng)類型推斷,并要求顯示的初始化,當(dāng)定義類型的名字很長(zhǎng)的時(shí)候就可以使用auto自動(dòng)推斷,亦或者在使用范圍for的時(shí)候更為方便

    3.內(nèi)存泄露的問(wèn)題會(huì)導(dǎo)致程序的崩潰,新增的智能指針就可以在進(jìn)程不正常結(jié)束的時(shí)候,能夠保證指針?biāo)赶虻膮^(qū)域正常的釋放(free);智能指針本質(zhì)就是定義了一個(gè)類,讓他具有指針的功能(因?yàn)轭悤?huì)自動(dòng)調(diào)用析構(gòu)函數(shù))

    4.在SLT中新增了移動(dòng)構(gòu)造和移動(dòng)拷貝,這就引入了左值和右值的概念,左值可以取他的地址,可以對(duì)他賦值,左值可以出現(xiàn)在賦值符號(hào)的左邊右值不能出現(xiàn)在賦值符號(hào)的左邊,比如一般的字面常量就是右值{1,'a',"str"}都算是右值。一般情況下左值的值都是有用的,而右值是可以被奪取的,在類的構(gòu)造的時(shí)候會(huì)識(shí)別參數(shù)是左值還是右值,如果是左值就進(jìn)行正常的構(gòu)造,如果是右值就進(jìn)行移動(dòng)構(gòu)造,移動(dòng)構(gòu)造就是用參數(shù)的資源來(lái)構(gòu)建自己,有點(diǎn)成全他人犧牲自己,所以移動(dòng)構(gòu)造只是資源的轉(zhuǎn)移而不會(huì)新增資源。

    5.lambda表達(dá)式很大程度上方便了代碼的控制,就像sort函數(shù)時(shí)一般情況下是默認(rèn)升序,而想讓sort后降序就需要傳遞一個(gè)仿函數(shù)過(guò)去,這樣就導(dǎo)致我們要特地去寫(xiě)一個(gè)仿函數(shù)僅僅為了讓sort排降序,所以lambda表達(dá)式就是一個(gè)匿名函數(shù)可以在一定程度上代替掉仿函數(shù)。

    如果文章對(duì)你有幫助還請(qǐng)不要浪費(fèi)你的點(diǎn)贊哦,謝謝支持


    ?

    總結(jié)

    以上是生活随笔為你收集整理的C++11 特性的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。