雖然我也用了 C++ 有挺多年了,但是一直本著夠用就行的原則,沒(méi)有特別深入的學(xué)習(xí)過(guò)C++ 的語(yǔ)法,所以好多高級(jí)的 C++ 特性都不了解。正好最近從網(wǎng)上找到了本書(shū)《C++ 14 Quick Syntax Reference》,挺薄的一本書(shū),只有 100多頁(yè),但是覆蓋了基本所有 C++ 的特性。這個(gè)小短文就是我看這本書(shū)時(shí)摘抄下來(lái)的一些我以前沒(méi)有注意到的知識(shí)點(diǎn)。
文中所有代碼都在 gcc version 5.3.0 (Rev1, Built by MSYS2 project) 上測(cè)試通過(guò)。
16進(jìn)制、8進(jìn)制、2進(jìn)制表示
int myOct = 062;
int myHex = 0x32;
int myBin = 0b00110010;
其中 16進(jìn)制、8進(jìn)制表示法是很早就支持的特性。2進(jìn)制表示是 C++14 才正式支持的。
除此之外,C++14 還引入了單引號(hào)作為數(shù)字表示的分隔符。方便我們閱讀很長(zhǎng)的數(shù)字。比如下面這個(gè)例子:
int longBin = 0b1010'0101'1010'0101;
加了三個(gè)單引號(hào)作為分割,讀起來(lái)就方便多了。
NULL 指針(nullptr)
早期的 C++ 中,我們用 0 或者 NULL 來(lái)表示無(wú)效的指針地址。C++11 專(zhuān)門(mén)引入了一個(gè)新的關(guān)鍵字 nullptr 來(lái)表示 NULL 指針。
int* p = nullptr; // ok
并且 nullptr 還是有類(lèi)型的,類(lèi)型為 nullptr_t:
nullptr_t mynull = nullptr; // ok
右值引用類(lèi)型(C++11)
左值和右值都是針對(duì)表達(dá)式而言的,左值是指表達(dá)式結(jié)束后依然存在的持久對(duì)象,右值是指表達(dá)式結(jié)束時(shí)就不再存在的臨時(shí)對(duì)象。
所謂右值引用就是引用一個(gè)右值對(duì)象(臨時(shí)對(duì)象),比如下面的例子:
int&& ref = 1 + 2; // rvalue reference
ref += 3;
cout << ref; // "6"
ref 對(duì)應(yīng)的是 1+2的結(jié)果3,這是個(gè)臨時(shí)對(duì)象。傳統(tǒng)的C++引用方式是無(wú)法引用到這個(gè)臨時(shí)對(duì)象的。右值引用主要是解決效率問(wèn)題,具體的方法可以搜索 “C++ 移動(dòng)語(yǔ)義”。
原生字符串(raw string literals)
raw string 可以取消轉(zhuǎn)義字符的作用,是 C++11 中添加的新特性。比如:
string escaped = "c:\\Windows\\System32\\cmd.exe";
可以簡(jiǎn)寫(xiě)為:
string raw = R"(c:\Windows\System32\cmd.exe)";
RAW String 開(kāi)頭要用大寫(xiě)的 R ,然后在雙引號(hào)內(nèi)要增加一對(duì)括號(hào)。
這個(gè)功能在其他的語(yǔ)言中早就有了。我們上邊這個(gè)例子還不大能看出 raw string 的優(yōu)勢(shì),但是如果常寫(xiě)正則表達(dá)式的話(huà),就會(huì)感覺(jué) raw string 太方便了。比如:
char str[] = R"(('(?:[^\\']|\\.)*'|"(?:[^\\"]|\\.)*")|)";
舊的寫(xiě)法是:
char oldstr[] = "('(?:[^\\\\']|\\\\.)*'|\"(?:[^\\\\\"]|\\\\.)*\")|";
新的 char 類(lèi)型
C++ 11 中引入了 char16_t 和 char32_t 兩種類(lèi)型。這兩種字符類(lèi)型可以分別用來(lái)存儲(chǔ) utf-16 和 utf-32 編碼的字符。相應(yīng)的 string 類(lèi)型也有兩個(gè)變種, u16string 和u32string。
string s3 = u8"UTF-8 string";
u16string s4 = u"UTF-16 string";
u32string s5 = U"UTF-32 string";
字符串前的 u8、u和 U也是新增的特性,分別用來(lái)支持 UTF-8、UTF-16和UTF-32 字符串。
for 循環(huán)
C++11 對(duì) for 循環(huán)做了擴(kuò)展,for 循環(huán)支持一種新的語(yǔ)法。
int a[3] = {1, 2, 3};
for (int &i : a)
{
cout <<i; // "123"
}
auto 和 decltype 關(guān)鍵字
這兩個(gè)關(guān)鍵字都是 C++ 11 中引入的。auto 關(guān)鍵字告訴編譯器自動(dòng)推導(dǎo)變量的類(lèi)型。比如下面的代碼:
auto i = 5; // int
auto d = 3.14; // double
auto b = false; // bool
如果我們希望推導(dǎo)出的類(lèi)型是引用類(lèi)型。那么需要在 auto 之后加個(gè) &。比如下面這樣:
int& iRef = i;
auto myAuto = iRef; // int
auto& myRef = iRef; // int&
用 auto 之后可以簡(jiǎn)化許多代碼。比如下面這個(gè)代碼:
vector<int> myVector { 1, 2, 3 };
for(vector<int>::size_type i = 0; i != myVector.size(); i++)
{
cout << myVector[i];
}
用 auto 的話(huà)可以寫(xiě)為:
for(auto i = 0; i != myVector.size(); i++)
{
cout << myVector[i];
}
當(dāng)然,用上 for 的新語(yǔ)法,還可以寫(xiě)的更簡(jiǎn)便:
for (auto& x : myVector)
{
cout << x << endl;
}
decltype 與 auto 有些類(lèi)似,它可以用來(lái)推導(dǎo)一個(gè)表達(dá)式的類(lèi)型,比如下面的例子:
int a = 0b10'001'000;
cout << a << endl;
decltype(a) c = a + 1; //int
cout << c << endl;
decltype(3) b = 3; // int&&
需要解釋一下的是這個(gè)例子中 3 是個(gè)臨時(shí)變量。所以推導(dǎo)出 b 的類(lèi)型是一個(gè) int 型的右值引用。但是作為函數(shù)返回值時(shí),推導(dǎo)出的就不是右值引用了。
decltype(5) getFive() { return 5; } // int
C++ 11 中 auto 和 decltype 還可以配合使用,用來(lái)推導(dǎo)函數(shù)的返回值類(lèi)型。下面是個(gè)例子:
auto getValue(int x) -> decltype(x) { return x; } // int
這么寫(xiě)還是挺繁瑣的, C++ 14 中做了簡(jiǎn)化??梢院?jiǎn)單的寫(xiě)為:
auto getValue(int x) { return x; } // int
不過(guò)我感覺(jué)這兩種函數(shù)寫(xiě)法作用都不大,因?yàn)槲覀儧](méi)法在頭文件中把函數(shù)聲明寫(xiě)為:
auto getValue(int x);
因?yàn)闆](méi)有函數(shù)體,根本無(wú)法做類(lèi)型推導(dǎo)…
C++14 中還支持如下的寫(xiě)法:
decltype(auto) = 3; // int&&
decltype(auto) getRef(int& x) { return x; } // int&
這些寫(xiě)法知道也就行了,用處不大。
Lambda 函數(shù)
Lambda 函數(shù)的概念最早應(yīng)該來(lái)源于 Lisp 語(yǔ)言,現(xiàn)在也被 C++ 11 吸收進(jìn)來(lái)了。
Lambda 函數(shù)使得我們可以像定義一個(gè)變量一樣定義一個(gè)函數(shù)。比如下面這樣:
auto sum = [](int x, int y) -> int {return x + y;};
cout << sum(2, 3);
上面的函數(shù)還可以簡(jiǎn)寫(xiě)為:
auto sum = [](int x, int y) { return x + y; };
編譯器會(huì)自動(dòng)推導(dǎo)返回值的類(lèi)型。
到了 C++ 14 Lambda 函數(shù)更是支持了泛型。
auto sum = [](auto x, auto y) {return x + y;};
cout << sum(2, 3) << endl;
cout << sum(2.2, 3.0) << endl;
cout << sum(2.2, 3) << endl;
Lambda 函數(shù)也可以作為函數(shù)的參數(shù)傳遞給函數(shù)。下面是個(gè)例子:
#include <iostream>
#include <functional>
using namespace std;
void call(int arg, function<void(int)> func)
{
func(arg);
}
int main()
{
auto printSquare = [](int x) { cout << x*x; };
call(2, printSquare); // "4"
}
上面的例子其實(shí)還說(shuō)明 Lambda 函數(shù)不是普通的函數(shù)。它是一種特殊類(lèi)型的對(duì)象。比如下面的 Lambda 函數(shù):
auto sum = [](int x, int y) {return x + y;};
寫(xiě)完整了應(yīng)該是 :
function<int(int)> sum = [](int x, int y) {return x + y;};
Lambda 函數(shù)還有一些高級(jí)用法。比如Lambda 函數(shù)中的 “[]” 是有作用的。用它可以將所在域的其他變量引入到函數(shù)中。比如下面的例子:
void call(function<void()> func) { func(); }
int main()
{
int i = 2;
auto printSquare = [i]() { cout << i*i; };
call(printSquare); // "4"
}
Lambda 函數(shù)中 [] 里按值傳進(jìn)去的參數(shù)是只讀的。所以下面的代碼是錯(cuò)的:
int a = 10;
int b = [a](int i) { a++; return a * i; } (5); // 50
我們可以添加一個(gè) mutable 關(guān)鍵字使得 a 不是只讀,但是 a 值的改變是不會(huì)影響函數(shù)外面的。
int a = 10;
int b = [a](int i) mutable { a++; return a * i; } (5);
cout << b << endl; // 55
cout << a << endl; // 10
Lambda 函數(shù)還可以是無(wú)名函數(shù),這時(shí)定義函數(shù)的同時(shí)也要調(diào)用這個(gè)函數(shù)。否則因?yàn)檫@個(gè)函數(shù)沒(méi)有名字,之后就沒(méi)有調(diào)用的機(jī)會(huì)了。下面這兩種寫(xiě)法結(jié)果是相同的。
cout << [](int i) { return i*i; } (101) << endl;
auto printSquare = [](int i) { return i*i; };
cout << printSquare(101) << endl;
利用 “[]” 也可以把結(jié)果傳出來(lái)。下面這兩種方法結(jié)果也是相同的。
int a = [](int i) { return i * i; } (11);
cout << a << endl;
[&a] (int i){ a = i * i;}(12);
cout << a << endl;
C++ 14 還支持一些新的特性,比如下面這樣:
int a = 1;
[&, b = 2]() { a += b; }();
cout << a; // "3"
關(guān)于 Lambda 函數(shù),知道這些也就差不多了。
第一篇先寫(xiě)這么多。下一篇寫(xiě)寫(xiě)關(guān)于類(lèi)和對(duì)象的一些新特性。
|