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