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