1. 程式人生 > >C++11 中值得關註的幾大變化(一)

C++11 中值得關註的幾大變化(一)

表達 strip array 通過 var arc 個人 模式 case

技術分享圖片

首先呢,小編覺得c++17雖然已經出來了,c++20也即將面世,但是並不代表著C++11 的沒落,我們現在所學到的C++知識大多還是C++11 的,並不是書本不更新,而是其仍然起著重要的作用!接下來我們就來看看C++11中的一些值得關註的幾大變化吧!

Lambda 表達式

Lambda表達式來源於函數式編程,說白就了就是在使用的地方定義函數,有的語言叫“閉包”,如果 lambda 函數沒有傳回值(例如 void ),其回返類型可被完全忽略。 定義在與 lambda 函數相同作用域的變量參考也可以被使用。這種的變量集合一般被稱作 closure(閉包)。我在這裏就不再講這個事了。表達式的簡單語法如下,

[capture](parameters)->return_type {body}

原文的作者給出了下面的例子:

intmain()

{

chars[]="Hello World!";

intUppercase = 0;//modified by the lambda

for_each(s, s+sizeof(s), [&Uppercase] (charc) {

if(isupper(c))

Uppercase++;

});

cout<< Uppercase << " uppercase letters in: " << s <

}

在傳統的STL中for_each() 這個玩意最後那個參數需要一個“函數對象”,所謂函數對象,其實是一個class,這個class重載了operator(),於是這個對象可以像函數的式樣的使用。實現一個函數對象並不容易,需要使用template,比如下面這個例子就是函數對象的簡單例子(實際的實現遠比這個復雜):

template<classT>

classless

{

public:

booloperator()(const T&l, const T&r)const

{

returnl < r;

}

};

C++引入Lambda的最主要原因就是:1)可以定義匿名函數,2)編譯器會把其轉成函數對象

那麽,除了方便外,為什麽一定要使用Lambda呢?它比傳統的函數或是函數對象有什麽好處呢?我個人所理解的是,這種函數之年以叫“閉包”,就是因為其限制了別人的訪問,更私有。也可以認為他是一次性的方法。Lambda表達式應該是簡潔的,極私有的,為了更易的代碼和更方便的編程。

自動類型推導 auto

在這一節中,原文主要介紹了兩個關鍵字 auto 和 deltype,示例如下:

1.

auto x=0;//x has type int because 0 is int

auto c=‘a‘;//char

auto d=0.5;//double

auto national_debt=14400000000000LL;//long long

2.

vector<int>::const_iterator ci = vi.begin();

可以變成:

auto ci = vi.begin();

模板這個特性讓C++的代碼變得很難讀,不信你可以看看STL的源碼,那是一個亂啊。使用auto必需一個初始化值,編譯器可以通過這個初始化值推導出類型。因為auto是來簡化模板類引入的代碼難讀的問題,如上面的示例,iteration這種類型就最適合用auto的,但是,我們不應該把其濫用。

比如下面的代碼的可讀性就降低了。因為,我不知道ProcessData返回什麽?int? bool? 還是對象?或是別的什麽?這讓你後面的程序不知道怎麽做。

auto obj = ProcessData(someVariables);

但是下面的程序就沒有問題,因為pObject的型別在後面的new中有了。

auto pObject =newSomeType::SomeOtherType();

自動化推導 decltype

關於 decltype 是一個操作符,其可以評估括號內表達式的類型,其規則如下:

如果表達式e是一個變量,那麽就是這個變量的類型。

如果表達式e是一個函數,那麽就是這個函數返回值的類型。

如果不符合1和2,如果e是左值,類型為T,那麽decltype(e)是T&;如果是右值,則是T。

原文給出的示例如下,我們可以看到,這個讓的確我們的定義變量省了很多事。

const vector<int> vi;

typedefdecltype(vi.begin()) CIT;

CIT another_const_iterator;

還有一個適合的用法是用來typedef函數指針,也會省很多事。比如:

decltype(&myfunc) pfunc = 0;

typedefdecltype(&A::func1) type;

auto 和 decltype 的差別和關系

Wikipedia 上是這麽說的(關於decltype的規則見上)

#include <vector>

intmain()

{

const std::vector<int> v(1);

auto a = v[0];// a 的類型是 int

decltype(v[0]) b = 1;// b 的類型是 const int&, 因為函數的返回類型是

// std::vector<int>::operator[](size_type) const

auto c = 0;// c 的類型是 int

auto d = c;// d 的類型是 int

decltype(c) e;// e 的類型是 int, 因為 c 的類型是int

decltype((c)) f = c;// f 的類型是 int&, 因為 (c) 是左值

decltype(0) g;// g 的類型是 int, 因為 0 是右值

}

如果auto 和 decltype 在一起使用會是什麽樣子?能看下面的示例,下面這個示例也是引入decltype的一個原因——讓C++有能力寫一個模板”

template<typenameLHS,typenameRHS>

auto AddingFunc(const LHS &lhs, const RHS &rhs) ->decltype(lhs+rhs)

{returnlhs + rhs;}

這個函數模板看起來相當費解,其用到了auto 和 decltype 來擴展了已有的模板技術的不足。怎麽個不足呢?在上例中,我不知道AddingFunc會接收什麽樣類型的對象,這兩個對象的 + 操作符返回的類型也不知道,老的模板函數無法定義AddingFunc返回值和這兩個對象相加後的返回值匹配,所以,你可以使用上述的這種定義。

統一的初始化語法

C/C++的初始化的方法比較,C++ 11 用大括號統一了這些初始化的方法。

比如:POD的類型。

intarr[4]={0,1,2,3};

structtm today={0};

關於POD相說兩句,所謂POD就是Plain Old Data,當class/struct是極簡的(trivial)、屬於標準布局(standard-layout),以及他的所有非靜態(non-static)成員都是POD時,會被視為POD。如:

structA {intm; };// POD

structB { ~B();intm; };// non-POD, compiler generated default ctor

structC { C() : m() {}; ~C();intm; };// non-POD, default-initialising m

POD的初始化有點怪,比如上例,new A; 和new A(); 是不一樣的,對於其內部的m,前者沒有被初始化,後者被初始化了(不同 的編譯器行為不一樣,VC++和GCC不一樣)。而非POD的初始化,則都會被初始化。&lt;/span&gt;

從這點可以看出,C/C++的初始化問題很奇怪,所以,在C++ 2011版中就做了統一。原文作者給出了如下的示例:

C c {0,0};//C++11 only. 相當於: C c(0,0);

int* a =newint[3] { 1, 2, 0 }; /C++11 only

classX {

inta[4];

public:

X() : a{1,2,3,4} {}//C++11, member array initializer

};

容器的初始化:

// C++11 container initializer

vector<string> vs={ "first", "second", "third"};

map singers =

{ {"Lady Gaga", "+1 (212) 555-7890"},

{"Beyonce Knowles", "+1 (212) 555-0987"}};

還支持像Java一樣的成員初始化:&lt;/span&gt;

classC

{

inta=7;//C++11 only

public:

C();

};

Delete 和 Default 函數

我們知道C++的編譯器在你沒有定義某些成員函數的時候會給你的類自動生成這些函數,比如,構造函數,拷貝構造,析構函數,賦值函數。有些時候,我們不想要這些函數,比如,構造函數,因為我們想做實現單例模式。傳統的做法是將其聲明成private類型。

在新的C++中引入了兩個指示符,delete意為告訴編譯器不自動產生這個函數,default告訴編譯器產生一個默認的。原文給出了下面兩個例子:

structA

{

A()=default;//C++11

virtual ~A()=default;//C++11

};

再如delete

structNoCopy

{

NoCopy &operator=( const NoCopy & ) = delete;

NoCopy ( const NoCopy & ) = delete;

};

NoCopy a;

NoCopy b(a);//compilation error, copy ctor is deleted

這裏,我想說一下,為什麽我們需要default?我什麽都不寫不就是default嗎?不全然是,比如構造函數,因為只要你定義了一個構造函數,編譯器就不會給你生成一個默認的了。所以,為了要讓默認的和自定義的共存,才引入這個參數,如下例所示:

structSomeType

{

SomeType() =default;// 使用編譯器生成的默認構造函數

SomeType(OtherTypevalue);

};

關於delete還有兩個有用的地方是

1)讓你的對象只能生成在棧內存上:

structNonNewable {

void*operatornew(std::size_t) = delete;

};

2)阻止函數的其形參的類型調用:(若嘗試以 double 的形參調用 f(),將會引發編譯期錯誤, 編譯器不會自動將 double 形參轉型為 int 再調用f(),如果傳入的參數是double,則會出現編譯錯誤)

voidf(inti);

voidf(double) = delete;

nullptr

C/C++的NULL宏是個被有很多潛在BUG的宏。因為有的庫把其定義成整數0,有的定義成 (void*)0。在C的時代還好。但是在C++的時代,這就會引發很多問題。你可以上網看看。這是為什麽需要 nullptr 的原因。 nullptr 是強類型的。

voidf(int);//#1

voidf(char*);//#2

//C++03

f(0);//二義性

//C++11

f(nullptr)//無二義性,調用f(char*)

所以在新版中請以 nullptr 初始化指針。

註:喜歡小編文章可以收藏!有興趣學習C/C++的小夥伴可以進群:941636044

C++11 中值得關註的幾大變化(一)