C++ 11和C++98相比有哪些新特性
C++11標(biāo)準(zhǔn)提供了許多有用的新特性。這篇文章特別針對(duì)使C++11和C++98相比看上去像一門(mén)新語(yǔ)言的特性,因?yàn)椋?/p>
C++11改變了書(shū)寫(xiě)C++代碼的風(fēng)格和習(xí)慣,也改變了設(shè)計(jì)C++庫(kù)的方式。例如,你會(huì)看到更多的被當(dāng)作參數(shù)和返回值的智能指針,還有按值(by value)返回巨大對(duì)象的函數(shù)。
它們被使用的非常廣泛,在大多數(shù)代碼中你都能看到它們。舉個(gè)例子,在現(xiàn)代C++中幾乎每5行C++代碼你就能看到auto關(guān)鍵字。
還有一些其它的非常好的C++11新特性,但先把這篇文章所描述的新特性熟悉起來(lái)把,因?yàn)檫@些被廣泛使用的特性展示了為什么C++11代碼是簡(jiǎn)潔的,安全的和快速的,就像其它現(xiàn)代主流開(kāi)發(fā)語(yǔ)言一樣,并且性能和傳統(tǒng)C++一樣強(qiáng)大。
1. Auto
在任何可能的時(shí)候使用auto。因?yàn)橛袃蓚€(gè)原因。第一,非常明顯,能夠避免重復(fù)輸入我們已經(jīng)聲明的并且編譯器已經(jīng)認(rèn)識(shí)的類(lèi)型名稱(chēng),這是非常方便的。
// C++98 map<int,string>::iterator i = m.begin(); double const xlimit = config["xlimit"]; singleton& s = singleton::instance(); // C++11 auto i = begin(m); auto const xlimit = config["xlimit"]; auto& s = singleton::instance();
第二,當(dāng)有遇到一個(gè)你不知道或者無(wú)法用語(yǔ)言表達(dá)的類(lèi)型時(shí),auto就不僅僅是使用方便這么簡(jiǎn)單了,比如,大多數(shù)lambda函數(shù)的類(lèi)型,你不能夠容易的將其類(lèi)型拼寫(xiě)出來(lái)甚至根本就不能夠?qū)懗鰜?lái)。
// C++98 binder2nd< greater > x = bind2nd( greater(), 42 ); // C++11 auto x = [](int i) { return i > 42; };
注意,使用auto并沒(méi)有修改代碼的語(yǔ)義。代碼仍然是靜態(tài)輸入(statically typed)的,并且每個(gè)表達(dá)式都干凈利落;只是不再?gòu)?qiáng)制我們多余的重新聲明類(lèi)型的名稱(chēng)。
一些人開(kāi)始的時(shí)候害怕使用auto,因?yàn)榻o人的感覺(jué)像是并沒(méi)有聲明(重新聲明)我們想要的類(lèi)型,這意味著我們可能會(huì)突然得到一個(gè)不同的類(lèi)型。如果你想顯示的做強(qiáng)制類(lèi)型轉(zhuǎn)換,這沒(méi)有問(wèn)題;聲明目標(biāo)類(lèi)型就可以了。但是在大部分情況下,使用auto就足夠了,由于出現(xiàn)錯(cuò)誤而得到另外一個(gè)類(lèi)型的情況很少見(jiàn),在使用強(qiáng)靜態(tài)類(lèi)型(strong static typing)情況下,如果類(lèi)型出現(xiàn)錯(cuò)誤編譯器就會(huì)告訴你。
2. 智能指針,no delete
總是使用智能指針,不要用原生指針和delete。除非需要實(shí)現(xiàn)你自己的底層數(shù)據(jù)結(jié)構(gòu)(把原生指針很好的封裝在類(lèi)(class boundary)中
如果你知道你是另外一個(gè)對(duì)象的唯一擁有著,使用unique_ptr來(lái)表示唯一的擁有權(quán)。一個(gè)"new T"表達(dá)式能很快的初始化一個(gè)擁有 這個(gè)智能指針的對(duì)象,特別是unique_ptr。典型的例子是指向?qū)崿F(xiàn)的指針(Pimpl Idiom):
// C++11 Pimpl idiom: header file class widget { public: widget(); // ... (see GotW #100) ... private: class impl; unique_ptr<impl> pimpl; }; // implementation file class widget::impl { /*...*/ }; widget::widget() : pimpl{ new impl{ /*...*/ } } { } // ...
使用shared_ptr來(lái)表示共享所有權(quán)(shared ownership)。使用make_shared來(lái)創(chuàng)建共享對(duì)象更好。
// C++98 widget* pw = new widget(); ::: delete pw; // C++11 auto pw = make_shared<widget>();
使用weak_ptr來(lái)打破循環(huán)和表示可選性(比如實(shí)現(xiàn)一個(gè)對(duì)象緩存)
// C++11 class gadget; class widget { private: shared_ptr<gadget> g; // if shared ownership }; class gadget { private: weak_ptr<widget> w; };
如果你了解到另外一個(gè)對(duì)象比你的生存周期要長(zhǎng),并且你想觀察這個(gè)對(duì)象,那么使用原生指針(raw pointer)。
// C++11
class node {
vector<unique_ptr<node>> children;
node* parent;
public:
:::
};
3. Nullptr
用nullptr來(lái)表示一個(gè)空指針,不要再使用數(shù)字0或者宏NULL來(lái)表示空指針了,因?yàn)檫@些是模棱兩可的,既能表示整形也可表示指針。
// C++98
int* p = 0;
// C++11
int* p = nullptr;
4. Range for
對(duì)一個(gè)范圍內(nèi)的元素進(jìn)行有序訪問(wèn),基于range的for循環(huán)會(huì)是更方便的用法。
// C++98
for( vector<int>::iterator i = v.begin(); i != v.end(); ++i ) {
total += *i;
}
// C++11
for( auto d : v ) {
total += d;
}
5. 非成員begin和end
使用非成員函數(shù)begin(x)和end(x)(不是x.begin()和x.end()),因?yàn)閎egin(x)和end(x)是可擴(kuò)展的,能同所有容器類(lèi)型一塊工作——甚至數(shù)組也可以——并不是只針對(duì)提供了STL風(fēng)格的x.begin()和x.end()成員函數(shù)的容器。
如果你正在使用一個(gè)非STL集合類(lèi)型,這個(gè)類(lèi)型提供迭代器但不是STL風(fēng)格的x.begin()和x.end(),你可以對(duì)他的非成員函數(shù)begin()和end()進(jìn)行重載,這樣你就可以使用同STL容器同樣的風(fēng)格進(jìn)行編碼。標(biāo)準(zhǔn)中舉了一個(gè)例子:數(shù)組,并且提供了對(duì)象的begin和end函數(shù):
vector<int> v;
int a[100];
// C++98
sort( v.begin(), v.end() );
sort( &a[0], &a[0] + sizeof(a)/sizeof(a[0]) );
// C++11
sort( begin(v), end(v) );
sort( begin(a), end(a) );
6. Lambda函數(shù)和算法
Lambda表達(dá)式改變了游戲規(guī)則,它會(huì)時(shí)不時(shí)的改變你的編碼方式,這種方式優(yōu)雅并且快速。Lambda使現(xiàn)存STL算法實(shí)用性提高了百倍。
新增加的C++庫(kù)的設(shè)計(jì)都以支持lambad表達(dá)式為前提(例如:PPL),甚至有一些庫(kù)需要通過(guò)你編寫(xiě)lambda表達(dá)式來(lái)使用庫(kù)(例如:c++ AMP)。
這里有個(gè)例子,找到v中的>X并且<Y的第一個(gè)元素。在C++11中,最簡(jiǎn)單并且干凈的代碼是使用標(biāo)準(zhǔn)算法。
// C++98: write a naked loop (using std::find_if is impractically difficult)
vector<int>::iterator i = v.begin(); // because we need to use i later
for( ; i != v.end(); ++i ) {
if( *i > x && *i < y ) break;
}
// C++11: use std::find_if
auto i = find_if( begin(v), end(v), [=](int i) { return i > x && i < y; } );
想使用一個(gè)循環(huán)或者類(lèi)似的語(yǔ)言特性(language feature)但實(shí)際上在該語(yǔ)言中并不存在,怎么辦?將其實(shí)現(xiàn)成模板函數(shù)(庫(kù)算法)就可以了,多虧了lambda,使用它就像是用一個(gè)語(yǔ)言特性一樣的方便,但是更靈活,因?yàn)樗_實(shí)是一個(gè)庫(kù)而不是一個(gè)固定的語(yǔ)言特性。
// C#
lock( mut_x ) {
... use x ...
}
// C++11 without lambdas: already nice, and more flexible (e.g., can use timeouts, other options)
{
lock_guard<mutex> hold { mut_x };
... use x ...
}
// C++11 with lambdas, and a helper algorithm: C# syntax in C++
// Algorithm: template<typename T> void lock( T& t, F f ) { lock_guard hold(t); f(); }
lock( mut_x, [&]{
... use x ...
});
熟悉一下lambda吧,你會(huì)發(fā)現(xiàn)他們很有用,并不只是在c++中,它們已經(jīng)在幾個(gè)主流語(yǔ)言中得到支持并且流行開(kāi)來(lái)。
7. Move/&&
把move當(dāng)作是對(duì)拷貝的優(yōu)化最合適不過(guò)了,雖然它也包含其他方面的東西(像完美轉(zhuǎn)發(fā)(perfect forwarding))
move語(yǔ)義改變了我們?cè)O(shè)計(jì)API的方式。我們會(huì)越來(lái)越多的將函數(shù)設(shè)計(jì)成return by value。
// C++98: alternatives to avoid copying
vector<int>* make_big_vector(); // option 1: return by pointer: no copy, but don't forget to delete
:::
vector<int>* result = make_big_vector();
void make_big_vector( vector<int>& out ); // option 2: pass out by reference: no copy, but caller needs a named object
:::
vector<int> result;
make_big_vector( result );
// C++11: move
vector<int> make_big_vector(); // usually sufficient for 'callee-allocated out' situations
:::
auto result = make_big_vector(); // guaranteed not to copy the vector
如果你想獲得比copy更高效的辦法,對(duì)你的類(lèi)型使用move語(yǔ)義吧。
8. 統(tǒng)一初始化和初始化列表
沒(méi)有發(fā)生變化的:當(dāng)初始化一個(gè)non-POD或者auto的本地變量時(shí),繼續(xù)使用熟悉的不帶額外花括號(hào){}的=語(yǔ)法。
// C++98 or C++11
int a = 42; // still fine, as always
// C++ 11
auto x = begin(v); // no narrowing or non-initialization is possible
在其他情況中(特別是隨處可見(jiàn)的使用()來(lái)構(gòu)造對(duì)象),使用花括號(hào){}會(huì)更好。使用花括號(hào){}能避免一些潛在的問(wèn)題:你不會(huì)突然得到一個(gè)收縮轉(zhuǎn)換(narrowing conversions)后的值(比如,float轉(zhuǎn)換成int),也不會(huì)有偶爾突發(fā)的未初始化POD成員變量或者數(shù)組的存在,也能避免在c++98中會(huì)碰到的奇怪事:你的代碼編譯沒(méi)問(wèn)題,你需要的是變量但實(shí)際上你聲明了一個(gè)函數(shù),這都源于C++聲明語(yǔ)法的模糊不清,Scott Meyers的著名說(shuō)法:“C++最令人苦惱的解析”。通過(guò)使用新風(fēng)格的語(yǔ)法上面解析問(wèn)題會(huì)不復(fù)存在。
// C++98
rectangle w( origin(), extents() ); // oops, declares a function, if origin and extents are types
complex<double> c( 2.71828, 3.14159 );
int a[] = { 1, 2, 3, 4 };
vector<int> v;
for( int i = 1; i <= 4; ++i ) v.push_back(i);
// C++11
rectangle w { origin(), extents() };
complex<double> c { 2.71828, 3.14159 };
int a[] { 1, 2, 3, 4 };
vector<int> v { 1, 2, 3, 4 };
新的{}語(yǔ)法在幾乎任何地方都能出色的工作。
// C++98
X::X( /*...*/ ) : mem1(init1), mem2(init2, init3) { /*...*/ }
// C++11
X::X( /*...*/ ) : mem1{init1}, mem2{init2, init3} { /*...*/ }
最后,有時(shí)候傳遞不帶(type-named temporary)的函數(shù)參數(shù)是很方便的:
void draw_rect( rectangle );
// C++98
draw_rect( rectangle( myobj.origin, selection.extents ) );
// C++11
draw_rect( { myobj.origin, selection.extents } );
我不喜歡使用花括號(hào){}的唯一地方是在初始化一個(gè)非POD變量的時(shí)候,像 auto x= begin(v);使用花括號(hào)會(huì)使代碼不必要的丑陋,因?yàn)槲抑懒怂且粋€(gè)類(lèi)類(lèi)型,所以我不必?fù)?dān)心收縮轉(zhuǎn)換,并且現(xiàn)代編譯器已經(jīng)對(duì)額外的拷貝(或者額外move,如果類(lèi)型是move-enabled)進(jìn)行了優(yōu)化。
上一篇:c++利用stl set_difference對(duì)車(chē)輛進(jìn)出區(qū)域進(jìn)行判定
欄 目:C語(yǔ)言
下一篇:VC使用編譯時(shí)間作為版本號(hào)標(biāo)識(shí)的方法
本文標(biāo)題:C++ 11和C++98相比有哪些新特性
本文地址:http://mengdiqiu.com.cn/a1/Cyuyan/1719.html
您可能感興趣的文章
- 04-02c語(yǔ)言沒(méi)有round函數(shù) round c語(yǔ)言
- 01-10求子數(shù)組最大和的解決方法詳解
- 01-10深入理解C++中常見(jiàn)的關(guān)鍵字含義
- 01-10使用C++實(shí)現(xiàn)全排列算法的方法詳解
- 01-10c++中inline的用法分析
- 01-10用C++實(shí)現(xiàn)DBSCAN聚類(lèi)算法
- 01-10全排列算法的非遞歸實(shí)現(xiàn)與遞歸實(shí)現(xiàn)的方法(C++)
- 01-10C++大數(shù)模板(推薦)
- 01-10淺談C/C++中的static與extern關(guān)鍵字的使用詳解
- 01-10深入C/C++浮點(diǎn)數(shù)在內(nèi)存中的存儲(chǔ)方式詳解


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹(shù)的示例代碼(圣誕
- 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問(wèn)題方法
- 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語(yǔ)言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 04-02c語(yǔ)言函數(shù)調(diào)用后清空內(nèi)存 c語(yǔ)言調(diào)用
- 04-02func函數(shù)+在C語(yǔ)言 func函數(shù)在c語(yǔ)言中
- 04-02c語(yǔ)言的正則匹配函數(shù) c語(yǔ)言正則表達(dá)
- 04-02c語(yǔ)言用函數(shù)寫(xiě)分段 用c語(yǔ)言表示分段
- 04-02c語(yǔ)言中對(duì)數(shù)函數(shù)的表達(dá)式 c語(yǔ)言中對(duì)
- 04-02c語(yǔ)言編寫(xiě)函數(shù)冒泡排序 c語(yǔ)言冒泡排
- 04-02c語(yǔ)言沒(méi)有round函數(shù) round c語(yǔ)言
- 04-02c語(yǔ)言分段函數(shù)怎么求 用c語(yǔ)言求分段
- 04-02C語(yǔ)言中怎么打出三角函數(shù) c語(yǔ)言中怎
- 04-02c語(yǔ)言調(diào)用函數(shù)求fibo C語(yǔ)言調(diào)用函數(shù)求
隨機(jī)閱讀
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 01-11Mac OSX 打開(kāi)原生自帶讀寫(xiě)NTFS功能(圖文
- 01-10C#中split用法實(shí)例總結(jié)
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 01-10delphi制作wav文件的方法
- 01-10SublimeText編譯C開(kāi)發(fā)環(huán)境設(shè)置
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 04-02jquery與jsp,用jquery