C++11新特性總結 (二)

1. 範圍for語句

C++11 引入了一種更爲簡單的for語句,這種for語句能夠很方便的遍歷容器或其餘序列的全部元素html

vector<int> vec = {1,2,3,4,5,6};
for(int x: vec)
{
    cout<<x<<endl;
}

2. 尾置返回類型

要想引入尾置類型,咱們還得從複雜的類型聲明提及。若是咱們須要定義一個含有10個int元素的數組,通常是這樣的:ios

int arr[10] = {0};

若是要定義指向這個數組的指針呢:正則表達式

int (*p_arr)[10] = &arr; //注意:int *p_arr[10] 表示一個數組,有10個元素,元素類型是int*

若是要定義一個函數,這個函數接受一個char類型的參數,並返回一個指向10個int類型數組的指針呢:數組

int (*func(char x))[10];

這樣的聲明是否是看的頭都大了,其實咱們能夠簡化一點,通常狀況下咱們可使用別名進行簡化,好比:安全

typedef int ARR[10] ; // 定義一個類型 ARR這是一個數組類型,含有10個int類型的元素
using ARR = int[10] ; // 同上

再定義如上的函數:函數

ARR * func(char x) ; // 函數返回的類型是 ARR* 也就是指向具備10個int元素數組的指針

固然在C++11中咱們能夠用以前講到過的另一個關鍵字decltype:性能

decltype(arr) * func(char x) ; // decltype(arr)表達式會得到arr的類型

最後就輪到咱們本節要說的C++11的另一個特性,尾置返回類型,任何函數均可以使用尾置返回類型,這種形式對於返回類型比較複雜的函數最有效,好比上面的函數可使用以下方式:spa

auto func(char x) -> int(*) [10]; 

這種形式將函數的返回類型寫在函數聲明的最後面,而且在函數形參列表後面加上 -> 符號,而後緊接着是函數須要返回的類型,因爲函數的返回類型被放在了形參列表以後,因此在函數名前面使用一個 auto替代。指針

3. =default 生成默認構造函數

在C++的類中,若是咱們沒有定義構造函數,編譯器會爲咱們合成默認的無參構造函數,若是咱們定義了構造函數,則編譯器就不生成默認構造函數了,可是若是咱們定義構造函數同時也但願編譯器生成默認構造函數呢? C++11中能夠經過在構造函數的聲明中直接  =default的方式要求編譯器生成構造函數。htm

class ClassName{
	public:
		ClassName(int x);
		ClassName()=default; // 顯示要求編譯器生成構造函數
};

4. 類對象成員的類內初始化

class ClassName
{
        public:
                int x = 10; //C++11 以前是不容許的
};

5. lambda表達式與bind函數

lambda表達式是一個能夠被調用的代碼單元,至關於一個內聯函數,有參數和返回值以及函數體。可是跟函數不一樣的是,lambda表達式能夠定義在函數的內部,一個完整的lambda表達式具備以下形式:

[捕獲列表](參數列表) mutable -> 返回類型 {函數體}

int x = 10;
int y = 20;
auto f = [x,&y](int a ,int b){++y;return a+b+x+y;};
cout<<f(1,2)<<endl; //34
cout<<y<<endl;      //21

lambda能夠省略參數列表(若是沒有參數的話),能夠省略返回類型,可是不能省略捕獲部分與函數體部分,即便捕獲列表爲空,也要有一個空的[],lambda有兩種捕獲,一種是值捕獲,一種是引用捕獲。若是是值捕獲那麼lambda中得到的是捕獲的變量的副本,若是是引用捕獲則得到的是引用,能夠在lambda內部修改引用的變量的值,如上x是值捕獲,y是引用捕獲,lambda中默認是值捕獲,若是變量前面添加&則是引用捕獲,另外lambda中還有兩種形式的引用捕獲,例如[=]表示值捕獲全部可見的變量,而[&]則表示引用捕獲全部可見變量。若是但願值捕獲全部可見變量,可是又有個別變量採用引用捕獲呢,[=,&x]表示值捕獲全部可見變量,同時引用捕獲x。而[&,x]則表示引用捕獲全部可見變量,x採用值捕獲的方式。

有關bind函數,在不少地方咱們可使用函數替換lambda表達式,畢竟若是不少地方須要用到同一個lambda表達式,並且這個lambda表達式比較長的話,將其定義成函數應該是最好的。對於沒有捕獲列表的lambda表達式咱們能夠直接使用函數替代,例如:

void main()
{
	auto f=[](int x,int y){return x+y};
	f();
}

咱們能夠用下面的方式替代:

int f(int x,int y)
{
   return x+y;
}

void main()
{
	f();
}

與上面的lambda是等價的,可是對於有捕獲列表的lambda表達式應該怎麼處理呢,例如:

void main()
{
	int x = 10;
	int y = 20;
	auto f = [x,&y](int a ,int b){return a+b+x+y;}; //一個值捕獲,一個引用捕獲
	f(33,44);
}

若是轉換成函數的形式:

int x = 10;
int y = 20;
int f(int a,int b)
{
  return a+bx+y;
}

void main()
{
	f(33,44);
}

這是一種可行的方法,可是總不能把全部的捕獲變量定義成全局變量吧。如今的關鍵問題是lambda的捕獲表達式中的內容轉換成函數不可行,C++11提供了bind函數來完成這樣的操做。

#include <functional> //bind()
#include <iostream>
using namespace std;
using namespace std::placeholders; // _1,_2所在的命名空間
int f(int x,int y,int a,int b)
{
  return a+b+x+y;
}

void main()
{
  int x = 10;
  int y = 20;

  auto f_wrap = bind(f,x,y,_1,_2);
  cout<<f_wrap(33,44)<<endl; // _1,_2是佔位符,表示調用f_wrap的時候_1是第一個參數,_2是第二個參數。最終會被替換成調用  f(10,20,33,44)
}

若是引用類型的捕獲怎麼作呢,看下面的例子,用lambda是這樣的:

#include <iostream>
#include <functional>
using namespace std;
using namespace std::placeholders;
void main()
{
  int x = 10;
  ostream &o = cout;
  auto f =[&o](int a){o<<a<<endl;}; // 注意這裏的輸出對象是用的引用捕獲
  f(x);
}

使用bind是這樣的:

#include <iostream>
#include <functional>
using namespace std;
using namespace std::placeholders;
void f(ostream &o,int x)
{
  o<<x<<endl;
}
int main()
{
  int x = 10;
  auto f_wrap = bind(f,ref(cout),_1); //將變量的引用傳遞到bind中是個問題,爲此C++11提供了ref()函數用於得到引用
  f_wrap(x);
  return 0 ;
}

6. 智能指針share_ptr,unique_ptr

C++11中引入了幾種智能指針,智能指針可以自動釋放所指向的對象,其中shared_ptr容許多個指針指向同一個對象,unique_ptr則獨佔所指向的對象,咱們主要說明shared_ptr的使用。經過使用make_shared<type>()函數產生智能指針對象。

shared_ptr<int> p = make_shared<int>(40); // p指向一個值爲40的int對象
shared_ptr<string> p2 = make_shared<string>(10,'c'); //指向值爲'cccccccccc'的string對象

make_shared<type>()函數中傳遞的值要與對應的type的構造函數相匹配,實際上應該就是直接調用的對應type的構造函數。

咱們可使用new初始化的指針來初始化智能指針:

share_ptr<int> p (new int(40));
p.get(); // 使用share_ptr<type>的get()函數來得到其關聯的原始指針。

shared_ptr對象在離開其做用域(例如一個函數體),會自動釋放其關聯的指針指向的動態內存,就像局部變量那樣。另外多個shared_ptr能夠指向一個對象,當最後一個shared_ptr對象銷燬的時候纔會銷燬其關聯的那個對象的動態內存。這裏使用了引用記數。

7. 右值引用與move調用,移動構造函數

爲了支持移動操做,C++11中使用了一種稱爲右值引用的類型。移動操做是什麼呢,通常狀況下咱們將一個對象賦值給另外一個對象的時候會調用對象到拷貝構造函數或者拷貝賦值運算符。而移動構造函數和移動賦值運算符則用來將數據從一個對象移動到另外一個對象。在不少狀況下對象拷貝以後須要被銷燬,此時使用移動操做會大幅提升性能。右值引用被使用以後,其關聯的對象會被銷燬。右值引用使用兩個&&表示,例如 int && 表示右值引用,而int &則是左值。經過C++11標準庫提供的函數 std::move()能夠獲得某一對象的右值引用。

TestClass::TestClass(TestClass &&t) noexcept //移動構造函數不該該拋出任何異常
:x(t.x),y(t.y),z(t.z),p(t.p)
{
  t.p=nullptr; // 實現移動操做以後須要保證被移動的對象的析構是安全的,因此源對象的指針成員置爲空,隨後t的析構函數將會被自動調用,t被銷燬。
}

8. function

C++11中提供了名爲function的標準庫類型,定義在頭文件<functional>中,該類型用來存儲一個可調用的對象,統一了全部能夠像函數同樣調用的調用形式,例如:

#include <functional>
#include <iostream>
using namespace std;

int add(int x,int y)
{
	return x+y;
}

class Add
{
	public:
		int operator()(int x,int y)
		{
			return x+y;
		}
};

void main()
{
  //function模版的參數是函數調用形式,包括返回類型,以及參數列表個數和類型
	function<int(int,int)> f1 = add;  //函數指針
	function<int(int,int)> f2 = Add(); // 可調用類對象
	function<int(int,int)> f3 = [](int x,int y){return x+y;}; //lambda表達式
	
	cout<<f1(10,20)<<" "<<f2(30,40)<<" "<<f3(50,60)<<endl;
}

9. 其餘新增類型(array,forward_list,bitset,regex)

實際上C++11中除了一些語法特性新增以外,還增長了一些新的庫。例如array至關於咱們傳統使用的定長數組,支持隨機訪問,不能添加刪除元素,不能夠像vector同樣增加,使用array比傳統數組更加安全。

forward_list是C++11中增長的單向鏈表

regex則是C++11中新增的正則表達式庫

10. 總結

C++11中增長了一些庫,庫自己的使用不作過多介紹,能夠參考C++標準庫/STL源碼剖析,這都是用單獨的大部頭書籍講解的,有些特性和庫是我感受比較驚豔的,例如:

auto 定義:可讓編譯器自動推算定義的變量類型,而不須要寫長長的一串類型,特別是在含有迭代器的類型上。
decltype :能夠根據已知的變量來定義一個跟該變量同樣的類型。
lambda:我的認爲這是C++11中增長的最驚豔的特性之一,對應的還有bind()函數,其實這些內容是從Boost中來的。
智能指針:shared_ptr 雖然在之前的C++中有相似auto_ptr的智能指針,可是auto_ptr有一些缺陷並不算很好用。
function類型:標準庫的function類型定義了一種可調用對象,該類型統一了全部能夠看成函數同樣調用的調用形式,例如lambda,函數指針,重載了函數調用運算符()的類對象等,該特性也是參考了Boost庫。
regex庫:C++11中總算有了方便的regex可使用了。

 

C++11新特性總結 (一)

 

若是您以爲這篇文章對您有幫助,須要您的【贊】,讓更多的人也能看見哦

相關文章
相關標籤/搜索