C++11 新特性简介
1.auto
auto是舊關鍵字,在C++11之前,auto用來聲明自動變量,表明變量存儲在棧,很少使用。在C++11中被賦予了新的含義和作用,用于類型推斷。
auto關鍵字主要有兩種用途:一是在變量定義時根據初始化表達式自動推斷該變量的類型,二是在聲明或定義函數時作為函數返回值的占位符,此時需要與關鍵字decltype連用。
1.1用法示例
(1)auto用于推斷變量類型示例。
auto i = 42; //i is an int
auto l = 42LL; //l is an long long
auto p = new foo(); //p is a foo*
1
2
3
4
5
(2)聲明或定義函數時作為函數返回值的占位符。
auto不能用來聲明函數的返回值。但如果函數有一個尾隨的返回類型時,auto是可以出現在函數聲明中返回值位置。這種情況下,auto并不是告訴編譯器去推斷返回類型,而是指引編譯器去函數的末端尋找返回值類型。在下面這個例子中,函數返回值類型是operator+操作符作用在T、U類型變量上的返回值類型。
template<class T, class U> auto add(T t, U u) -> decltype(t + u){
return t + u;
}
1
2
3
2.decltype
decltype與auto關鍵字一樣,用于進行編譯時類型推導,不過它與auto還是有一些區別的。decltype的類型推導并不是像auto一樣是從變量聲明的初始化表達式獲得變量的類型,而是總是以一個普通表達式作為參數,返回該表達式的類型,而且decltype并不會對表達式進行求值[2]。
2.1decltype推導規則
(1)如果e是一個變量或者類成員訪問表達式,假設e的類型是T,那么的decltype(e)為T,decltype((e))為T&。
(2)如果e是一個解引用操作,那么decltype(e)和decltype((e))均為T&。
(3)否則decltype(e)與decltype((e))均為T。
2.2用法示例
(1)推導出表達式類型。
struct A { double x; };
const A* a = new A{0};
//第一種情況
decltype(a->x) y; // type of y is double
decltype((a->x)) z = y; // type of z is const double&,因為a一個常量對象指針
//第二種情況
int* aa=new int;
decltype(aa) y=aa; //type of y is int&,解引用操作
//第三種情況
decltype(5) y; //type of y is int
decltype((5)) y; //type of y is int
const int&& RvalRef() { return 1; }
decltype ((RvalRef())) var = 1; //type of var is const int&&
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(2)與using/typedef合用,用于定義類型。
using size_t = decltype(sizeof(0));//sizeof(a)的返回值為size_t類型
using ptrdiff_t = decltype((int)0 - (int)0);
using nullptr_t = decltype(nullptr);
vectorvec;
typedef decltype(vec.begin()) vectype;
for (vectype i = vec.begin; i != vec.end(); i++){
//...
}
1
2
3
4
5
6
7
8
9
顯而易見,與auto一樣,也提高了代碼的可讀性。
(3)泛型編程中結合auto,用于追蹤函數的返回值類型,這也是decltype的最大用途。
template <typename _Tx, typename _Ty>
auto multiply(_Tx x, _Ty y)->decltype(xy)
{
return xy;
}
1
2
3
4
5
3.nullptr
以前都是用0來表示空指針的,但由于0可以被隱式類型轉換為整形,這就會存在一些問題。關鍵字nullptr是std::nullptr_t類型的值,用來指代空指針。nullptr和任何指針類型以及類成員指針類型的空值之間可以發生隱式類型轉換,同樣也可以隱式轉換為bool型(取值為false)。但是不存在到整形的隱式類型轉換[3]。
int* p1 = NULL;
//或
int* p2 = nullptr;
1
2
3
4.constexpr
constexpr再C++11中用于申明常量表達式(const expression)。常量表達式是指值不會改變并且在編譯過程中就得到計算結果的表達式[4]。
const int i=3; //i是一個常量變量
const int j=i+1; //j是一個常變量,i+1是一個常量表達式
int k=23; //k的值可以改變,從而不是一個常變量
const int m=f(); //m不是常變量,m的值只有在運行時才會獲取。
1
2
3
4
5
6
7
一般來說,若果一旦認定變量是一個常量表達式,那就把它聲明為constexpr類型。
必須明確一點,在constexpr聲明中,如果定義了一個指針,限定符號constexpr僅僅對指針有效,與指針所指對象無關。
const int p=nullptr; //p是一個指向整型常量的指針(pointer to const)
constexpr int p1=nullptr; //p1是一個常量指針(const pointer)
1
2
5.noexcept
在C++11標準之前,C++在函數聲明中有exception specification(異常聲明)的功能,用來指定函數可能拋出的異常類型[5]。
voidFunc0() throw(runtime_error);
voidFunc1() throw();
voidFunc2();
1
2
3
函數Func0可能拋出runtime_error類型的異常;函數Func1不會拋出任何異常;函數Func2沒有異常說明,則該函數可以拋出任何類型的異常。
如果函數拋出了沒有在異常說明中列出的異常,則編譯器會調用標準庫函數unexpected。默認情況下,unexpected函數會調用terminate函數終止程序。
這種異常聲明的功能很少使用,因此在C++11中被棄用(實際仍可使用)。C++11引入noexcept,具有兩層含義,一個是修飾符,而是操作符。具體用法如下。
(1)修飾符示例。
voidFunc3() noexcept;
1
noexcept的功能相當于上面的throw(),表示函數不會拋出異常。如果noexcept修飾的函數拋出了異常,編譯器可以選擇直接調用std::terminate()終止程序運行。noexcept比throw()效率高一些。
voidFunc4() noexcept(常量表達式);
1
如果常量表達式的結果為true,表示該函數不會拋出異常,反之則有可能拋出異常。不帶常量表達式的noexcept相當于noexcept(true)。
(2)操作符示例。
上面noexcept的用法是其作為修飾符時的用法,實際上noexcept還可以作為操作符,常用于模板中。
template void func5() noexcept( noexcept(T()) ) {}
1
第2個noexcept就是一個操作符,如果其參數是一個有可能拋出異常的表達式,則返回值為false,那么func5有可能會拋出異常,否則返回值為true,func5為noexcept(true),不會拋出異常。
這樣函數是否會拋出異常,可以由表達式進行推導,使得c++11更好的支持泛型編程。
6.final和override
2012 年 3 月 22 日,GCC 4.7.0 正式發布。從這個版本開始,GCC 增加了許多新的 C++ 11 的特性。今天我們要介紹的是其中的一個特性:顯式地使用 final和override關鍵字[6]。
6.1final
(1)final用于修飾類。
final修飾類,可用于申明終結類。從此C++終于有申明終結類的關鍵字了。
struct B1 final { };
struct D1 : B1 { }; // 錯誤!不能從 final 類繼承!
1
2
3
上面的代碼是錯誤的,因為 D1 試圖繼承 B1,而 B1 被 final聲明為終結類,類似于Java的關鍵字的作用。
(2)final用于修飾虛函數。
final用于修飾虛函數,表明子類不能重寫該虛函數,為”終結虛函數“。例如:
struct B2
{
virtual void f() final {} // final 函數
};
struct D2 : B2
{
virtual void f() {}
};
1
2
3
4
5
6
7
8
9
這段代碼會出錯,因為D2::f重寫了B2::f,但是B2::f卻被聲明為 final 。
6.2override
假如我們繼承基類的虛函數,在重寫虛函數時寫錯了,參數類型不對或個數不對,但是編譯沒問題,造成了對基類同名函數的隱藏,運行時候和設計的不一樣,override就是輔助檢查是否正真重寫了繼承的虛函數。例如:
struct B3
{
virtual void f() {}
};
struct D3 : B3
{
void f(int a) {} //未重寫,發生隱藏,但不會報編譯錯誤
};
1
2
3
4
5
6
7
8
9
開發 D3 的程序員真的想重寫B3::f函數嗎?還是說,他只是不小心寫了個與父類同名的函數,卻在不經意間導致了隱藏?為了避免這種錯誤,C++ 11 引入了override關鍵字。于是,我們會發現,下面的一段代碼是會出錯的:
struct B4
{
virtual void g(int) {}
};
struct D4 : B4
{
virtual void g(int) override {} // OK
virtual void g(double) override {} // Error
};
1
2
3
4
5
6
7
8
9
10
多虧了override關鍵字,我們可以讓編譯器幫我們檢測到這個很難發現的程序錯誤。這段代碼的錯誤在于,override關鍵字表明,g(double)雖然想要進行override的操作,但實際父類并沒有這么個函數。在實際開發中,建議大家重寫繼承而來的虛函數時,加上關鍵字virtual表明當前函數式虛函數,C++編譯器的”放縱“降低了代碼的可讀性。
值得注意的是,這些并不是一些語法糖,而是能確確實實地避免很多程序錯誤,并且暗示編譯器可以作出一些優化。調用標記了final的virtual函數,例如上面的B2::f,GNU C++ 前端會識別出,這個函數不能被覆蓋,因此會將其從類的虛表中刪除。而標記為final的類,例如上面的 B1,編譯器則根本不會生成虛表。這樣的代碼顯然更有效率。
7.sizeof…運算符
sizeof…運算符的作用是獲取C++11中可變參數模板中參數包中元素個數。類似sizeof,sizeof…返回一個常量表達式,而且不會對模板的實參求值[7]。例如:
template<typename... Args> void g(Args... args){
cout<<sizeof...(Args)<<endl; //類型參數的數目
cout<<sizeof...(args)<<endl; //函數參數的數目
}
1
2
3
4
8.default和delete[8]
8.1default
我們知道,C++98和C++03編譯器在類中會隱式地產生四個函數:默認構造函數、拷貝構造函數、析構函數和賦值運算符函數,它們被稱為特殊成員函數。在 C++11 中,被稱為 “特殊成員函數” 的還有兩個:移動構造函數和移動賦值運算符函數。如果用戶申明了上面六種函數,編譯器則不會隱式產生。C++引入的default關鍵字,可顯示地、強制地要求編譯器為我們生成默認版本。
class DataOnly{
public:
DataOnly()=default; //default constructor
~DataOnly()=default; //destructor
};
1
2
3
4
5
6
7
8
9
10
11
上面的代碼,就可以讓編譯器生成上面六個函數的默認版本。
8.2delete
delete關鍵在C++11之前是對象釋放運算符,但在C++11中,被賦予了新的功能,主要有如下幾種作用:
(1)禁止編譯器生成上面六種函數的默認版本。
class DataOnly{
public:
DataOnly()=delete; //default constructor
~DataOnly()=delete; //destructor
};
1
2
3
4
5
6
7
8
9
10
11
(2)C++11 中,delete 關鍵字可用于任何函數,不僅僅局限于類成員函數。在函數重載中,可用delete來濾掉一些函數的形參類型,如下:
bool isLucky(int number); // original function
bool isLucky(char) = delete; // reject chars
bool isLucky(bool) = delete; // reject bools
bool isLucky(double) = delete; // reject doubles and floats
1
2
3
4
這樣在調用 isLucky 函數時,如果參數類型不對,則會出現錯誤提示
if (isLucky('a'))... // error! call to deleted function
if (isLucky(true))... // error!
if (isLucky(3.5))... // error!
1
2
3
(3)在模板特例化中,也可以用 delete 來過濾一些特定的形參類型。例如,Widget 類中聲明了一個函數模板,當進行模板特化時,要求禁止參數為 void* 的函數調用。
class Widget {
public:
template void processPointer(T* ptr){}
};
template<> void Widget::processPointer(void*)=delete; //deleted function template
1
2
3
4
5
9.static_assert
這個宏用于檢測和診斷編譯時錯誤,與assert(運行時斷言宏)相反。static_assert用于檢測編譯時程序的不變量。
一個表達式可以被計算為 bool 或 string (字符串),如果這個表達式的值為 false ,那么編譯器會出現一個包含特定字符串的錯誤,同時編譯失敗。如果為 true 那么沒有任何影響。例如:
static_assert(sizeof(void*) == 8,"not supported");
1
static_assert和type traits一起使用能發揮更大的威力。type traits是一些class template,在編譯時提供關于類型的信息。在頭文件中可以找到它們。這個頭文件中有好幾種class template,有helper class,用來產生編譯時常量,有type traits class,用來在編譯時獲取類型信息,還有就是type transformation class,他們可以將已存在的類型變換為新的類型。
下面這段代碼原本期望只做用于整數類型。
template <typename T1, typename T2> auto add(T1 t1, T2 t2)
{
return t1 + t2;
}
1
2
3
4
但是如果有人寫出如下代碼,編譯器并不會報錯
std::cout << add(1, 3.14) << std::endl;
std::cout << add("one", 2) << std::endl;
1
2
程序會打印出4.14和”e”。但是如果我們加上編譯時斷言,那么以上兩行將產生編譯錯誤。
template <typename T1, typename T2>
auto add(T1 t1, T2 t2)
{
static_assert(std::is_integral::value, "Type T1 must be integral");
static_assert(std::is_integral::value, "Type T2 must be integral");
return t1 + t2;
}
1
2
3
4
5
6
7
參考文獻
[1]【C++11新特性】auto關鍵字
[2]C++11特性:decltype關鍵字
[3]C++開發者都應該使用的10個C++11特性
[4]constexpr與常量表達式(c++11標準)
[5][了解C++11(五)—— noexcept]{http://www.xuebuyuan.com/2069091.html}
[6]C++11 新特性:顯式 override 和 final
[7]C++ primer中文版第五版:619-619
[8]C++11 之 delete 和 default
[9]< type_traits>
轉載于:https://www.cnblogs.com/Dennis-mi/articles/9166453.html
總結
以上是生活随笔為你收集整理的C++11 新特性简介的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 企业招聘宣传文案29句
- 下一篇: 给当兵朋友的祝福语163个