C++面試總結(五)C++ 11/14新特性
C++11是自C++98十餘年來發布的一個新特性,擴充了很多C++的功能和特性,而C++14是對C++11的又一次補充和優化,這些新特性使得C++更貼近於一種現代化的變成語言。gcc版本大於5(clang版本大於3.8)已經全面支援C++14。
1.Lambda 表示式
Lambda表示式,可以方便的定義和建立匿名函式。
Lambda表示式完整的宣告格式如下:
[capture list] (params list) mutable exception-> return type { function body }
各項具體含義如下
- capture list:捕獲外部變數列表
- params list:形參列表
- mutable指示符:用來說用是否可以修改捕獲的變數
- exception:異常設定
- return type:返回型別
- function body:函式體
“不完整”的Lambda表示式 :
[capture list] (params list) -> return type {function body}
[capture list] (params list) {function body}
[capture list] {function body}
示例:
#include <iostream> #include <vector> #include <algorithm> using namespace std; bool cmp(int a, int b) { return a < b; } int main() { vector<int> myvec{ 3, 2, 5, 7, 3, 2 }; vector<int> lbvec(myvec); sort(myvec.begin(), myvec.end(), cmp); // 舊式做法 cout << "predicate function:" << endl; for (int it : myvec) cout << it << ' '; cout << endl; sort(lbvec.begin(), lbvec.end(), [](int a, int b) -> bool { return a < b; }); // Lambda表示式 cout << "lambda expression:" << endl; for (int it : lbvec) cout << it << ' '; }
2.型別推導和判斷
C++11中引入auto第一種作用是為了自動型別推導。auto的自動型別推導,用於從初始化表示式中推斷出變數的資料型別。通過auto的自動型別推導,可以大大簡化我們的程式設計工作。
auto實際上實在編譯時對變數進行了型別推導,所以不會對程式的執行效率造成不良影響
另外,似乎auto並不會影響編譯速度,因為編譯時本來也要右側推導然後判斷與左側是否匹配。
auto i = 1; //OK auto i; //ERR,auto需要一個初始化值完成推導 int max(int, int) auto fun = max; //OK auto str = "ABC"; //OK int max(auto a, auto b); //ERR,因為過載的原因,不能這麼使用 for (auto iter = vec.cbegin(); iter != vec.cend(); iter++){}; //OK template <typename T1, typename T2, typename T3> auto add(T2 a, T3 b) { //僅在C++14中合法,c++11不支援 return a+b; }
型別判斷的引入主要是為了獲取變數的型別,使用decltype()
可以在編譯期間獲取變數的型別:
auto a = 1;
auto b = 2;
decltype(a+b) c; //ok
3.nullptr
和constexpr
引入nullptr
是為了解決NULL
的詬病,在之前的c++中,NULL
可以被定義為int
0或者一個0值的指標,這就帶來一個問題:
void fun(int);
void fun(void *);
fun(NULL); //無法確定使用的是哪一個過載函式,需要視NULL的定義而定
而nullptr
現在定義為一個空指標,避免了NULL帶來的問題。
constexpr
定義了一個使用者顯式的宣告函式或物件建構函式在編譯期間會成為常數,從 C++14 開始,constexptr
函式可以在內部使用區域性變數、迴圈和分支等簡單語句:
//這個函式會在編譯期間進行計算
constexpr int fun(const int n) {
if (1 == n) return 1;
else if (2 == n) return 1;
else return fun(n - 1) + fun(n - 2);
}
4.序列for迴圈
C++11引入了一種簡單的for語法用於快速迭代序列:
std::vector<int> a = {1, 2, 3, 4};
for (auto item : a) {
std::cout << item << std::endl;
}
5.初始化列表擴充套件
在引入C++11之前,只有陣列能使用初始化列表,其他容器想要使用初始化列表,只能用以下方法:
int arr[3] = {1, 2, 3}
vector<int> v(arr, arr + 3);
在C++11中,我們可以使用以下語法來進行替換:
int arr[3]{1, 2, 3};
vector<int> iv{1, 2, 3};
map<int, string>{{1, "a"}, {2, "b"}};
string str{"Hello World"};
6.類中預設函式行為
我們知道在沒有指定的情況下,c++會對類設定預設的建構函式、拷貝建構函式、賦值函式以及解構函式,但是有時候我們並不需要這些預設函式,因此在C++11中引入了對這些特性進行精確控制的特性:default指定生成預設函式,delete指定禁用預設函式。如果禁用了預設的建構函式和解構函式,必須指定一個自定義的函式。
class Test {
public:
Test() = default; //指定為Test類生成預設建構函式,如果設定為delete,就是禁用預設建構函式,如果禁用了
~Test() = default; //預設解構函式
Test(const Test&) = delete; //禁用拷貝建構函式
Test& operator=(const Test&) = delete; //禁用類賦值函式
};
Test a;
Test b(a); //error,因為已經被禁用
Test c = a; //error,因為已經被禁用
7.顯式控制虛擬函式過載
由於虛擬函式的特性,可能會被意外進行重寫,為了做到精確對虛擬函式過載的控制,c++11使用了override
和final
關鍵字完成對這一特性的實現,下面看例子:
class Test {
public:
Test() {};
virtual int fun(int);
};
class Test2 : public Test {
using Test::Test;
int fun(int) override; //顯式宣告對虛擬函式進行過載
int fun(float) override; //錯誤,父類沒有這個虛擬函式
}
而final
關鍵字是為了顯式終結類的繼承和虛擬函式的過載使用:
class Test {
public:
virtual int fun(int) final;
};
class Test2 final: public Test {
int fun(int) override; //非法,因為該虛擬函式已經設定為finale,禁止過載
};
class Test3 : public Test2 {}; //非法,Test2已經設定為final,禁止作為父類
8.模板增強
類型別名:使用using
關鍵字更加直觀的定義別名:
typedef int (*fun)(int *); //以前c++的做法,宣告一個引數為`int *`,返回值為int的函式指標,名字叫fun
using fun = int (*)(int *); //c++11,這樣更加直觀
template <typename T>
using newType = std::pair<T, T>;
變長模板和預設模板引數:
c++11可以在定義模板時,給與模板一個預設的型別,這樣可以在不設定型別的時候使用預設型別:
template <typename T = int, typename U = std::string>
同時c++11可以設定模板引數為任意個數:
template <typename T1, typename... TS> //可以接受至少一個模板型別
template <typename... TS> //至少0個
9.標準庫擴充
c++11對多個標準庫進行了擴充,包括:
1. 新增加容器
- 陣列容器:std::array
- 單項鍊表容器:std::forward_list
- 無序容器:std::unordered_set
- 無序對映:std::unordered_map
- 元組:std::tuple
2. 正則表示式
3. 語言級的執行緒支援
4. 智慧指標和引用計數
5. 函式繫結和包裝