【C++】auto關鍵字(c++11)
-
概念
C++11中,auto不再是一個儲存型別指示符,而是一個自動推導變數的型別,如:
#include <iostream> #include <typeinfo> using namespace std; int TestAuto() { return 10; } int main() { int a = 10; auto b = a;//由a是int,可以推匯出b的型別是int auto c = 'a';//由‘a’推匯出c的型別是char auto d = TestAuto(); cout << typeid(b).name() << endl; cout << typeid(c).name() << endl; cout << typeid(d).name() << endl; auto e;//這條語句編譯不通過,使用auto定義變數時,必須對其進行初始化 system("pause"); return 0; } //typeid(b).name()是列印型別名稱的函式
執行結果:
需要注意的是:
使用auto定義變數時,必須對其進行初始化,因為auto並非是一種型別的宣告,而是一個型別宣告時的“佔位符”,編譯器在編譯期間會將auto替換為變數實際的型別。
-
auto使用規則
1.auto與指標和引用結合起來使用。用auto宣告指標型別肘,用auto和auto*沒有任何區別,但用auto宣告引用型別吋必須加&
#include <iostream> #include <typeinfo> using namespace std; int main() { int x = 10; auto a = &x; auto* b = &x; auto& c = x; cout << typeid(a).name() << endl; cout << typeid(b).name() << endl; cout << typeid(c).name() << endl; system("pause"); return 0; }
2.在同一行定義多個變數時,這些變數必須是相同的型別,否則編譯器將會報錯,因為編譯器實際只對第一個型別進行推導,然後用推匯出來的型別定義其它變數。
#include <iostream> using namespace std; void TestAuto() { auto a = 1, b = 2; //auto c = 3, d = 4.0;//錯誤 auto c = 3, d = 4;//正確 cout << c << endl; cout << d << endl; } int main() { TestAuto(); system("pause"); return 0; }
-
auto不能自動推導的場景
1.auto不能作為函式的引數
void TestAuto(auto a)//此處程式碼不通過,auto不能作為形參型別,因為編譯器無法對a的實際型別進行自動推導
{
;
}
2.auto不能用來直接宣告陣列
void TestAuto()
{
int a[] = { 1, 2, 3 };
auto b[3] = a;//auto型別不能出現在頂級陣列型別中
}
3.為了避免與c++98中的auto發生混淆,c++11中只保留了auto作為型別推導的用法
4.auto最常見的就是會跟c++11中的新式for迴圈,還有lambda表示式進行配合使用
#include <iostream>
using namespace std;
int main()
{
int array[] = { 1, 2, 3, 4, 5 };
for (auto e : array)//依次取array裡面的元素讀給e
cout << e << endl;
system("pause");
return 0;
}
執行結果:
5.auto不能定義類的非靜態成員變數
6.例項化模板時不能使用auto作為模板引數
-
基於範圍的新式for迴圈(c++11)
在C++98中如果要遍歷一個數組,可以按照以下方式進行:
void TestFor()
{
intarray[]={1,2,3,4,5};
for (int i = 8; i < sizeof(array) / sizeof(array[0]); ++i )
array[i] *= 2;
for (int* p = array; p < array + sizeof(array)/ sizeof(array[0]); ++p)
cout << *p << endl;
}
對於一個有範圍的集合而言,由程式設計師來說明迴圈的範圍是多餘的,有時候還會容易犯錯誤。因此C++11中引入了基於範圍的for迴圈。for迴圈後的括號由冒號”:”分為兩部分:第一部分是範圍內用於迭代的變數,第二部分則表示被迭代的範圍。
int main()
{
int array[] = { 1, 2, 3, 4, 5 };
for (auto e : array)//依次取array裡面的元素讀給e
cout << e << " ";
system("pause");
return 0;
}
如果此時想要列印array陣列元素的2倍時,就必須加引用&:
#include <iostream>
using namespace std;
int main()
{
int array[] = { 1, 2, 3, 4, 5 };
for(auto& e : array)//遍歷陣列,給陣列的每個物件*2;
e = e * 2;
for (auto& e : array)//遍歷陣列,依次取出數組裡面的元素,賦給e
cout << e << " ";
system("pause");
return 0;
}
分析:不加“&”的意思是取出array裡面的每個物件,賦給e,e雖然被改變了,但數組裡面的物件並沒有變;加“&”表示e是數組裡面 每個物件的別名,此時e一改變,數組裡面的每個物件也會改變,因為e是每個物件的別名。
注意:與普通迴圈類似,可以用continue來結束本次迴圈, 也可以用break來跳出整個迴圈。
-
for的使用條件
1. for迴圈迭代的範圍必須是確定的,對於陣列而言,就是陣列中第一個元素和最後一個元素的範圍;對於類而言,應該提供begin和end的方法,begin和end就是for迴圈迭代的範圍,以下程式碼就有問題:
void Test(int array[])
{
for (auto& e : array)
cout << e <<endl;
}
分析:這個程式碼編譯不通過,此時的array不再是陣列而是一個指標,for的範圍不確定。
2.for迴圈要支援迭代器,還得支援++和==。
-
nullptr(c++11)
在C/C++程式設計習慣中,宣告一個變數時最好給該變數一個合適的初始值,否則可能會出現不可預料的錯誤,比如未初始化的指標。如果一個指標沒有合法的指向,我們基本都是按照如下方式對其進行初始化:
void TestPtr(){
int* p1 = NULL;int* p2 = 0;
而NULL實際是一個巨集, 在傳統的C標頭檔案(stddef.h)中,可以看到如下程式碼:
#ifndef NULL
#ifdef_ .cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
我們發現NULL在c++中被定義為整形常量0,在c語言中被定義為無型別指標(void* )的常量。這種定義,在使用空值的指標時,都不可避免的會遇到一些麻煩,比如:
#include <iostream>
using namespace std;
void f(int)
{
cout << "f(int)" << endl;
}
void f(int*)
{
cout << "f(int*)" << endl;
}
int main()
{
f(0);//調f(int)
f(NULL);//調f(int)
f((int*)NULL);//調f(int*)
system("pause");
return 0;
}
執行結果:
這個程式的本意是:想通過f(NULL)呼叫fint*)函式,但是由於NULL被定義成0,因此與程式的初衷相悖。在C++98中,字面常量0既可以是一個整形數字, 也可以是無型別的指標(Void)常量, 但是編譯器預設情況下將其看成是一個整形常量, 如果要將其按照指標方式來使用,必須對其進行強轉(void *)0。
-
nullptr
為了考慮相容性,C++11並沒有消除常量0的二義性,而是給出了全新的nullptr,表示空值指標。C++11為什麼不在NULL的基礎上進行擴充套件,這是因為NULL以前就是一個巨集, 而且不同的編譯器廠商對於NULL的實現可能不太相同,而且直接擴充套件NULL,可能會影響以前舊的程式。因此:為了避免混淆,C++11提供了nullptr,即: nullptr代表- 個指標空值常量。nullptr是有型別的, 其型別為nullptr. _t,僅僅可以被隱式轉化為指標型別,nullptr. _t被定義在標頭檔案中:
typedef decltype(nullptr) nullptr_ t;
需要注意的是:
1.在使用nullptr表示指標空值時,不需要包含標頭檔案,因為nullptr是C++11作為新關鍵字引入的。
2.在C++11中,sizeof(nullptr) 與sizeof(void*)0)所佔的位元組數相同。
3.為了提高程式碼的健壯性,建議在後續表示指標空值時,最好使用nullptr。
這裡還有常見的一類題:
請區分:NULL、0、'\0'、"\0"?
NULL:是被定義出來的一個巨集,它不是關鍵字,值為0;
0:是整形的0,值為0;
'\0':是字元0,值為0;
"\0":是字串;
計算:
#include <iostream>
using namespace std;
int main()
{
char str[] = "\0";
cout << strlen(str)<< endl;//0位元組
cout << sizeof(str) << endl;//2個位元組
system("pause");
return 0;
}
其中strlen(str)大小為0,因為轉義字元\+0,就是字串中的結束標誌"\0",只要遇到"\0",它就自動停下來了,所以strlen(str)大小為0;sizeof(str)大小為2個位元組,轉義字元\+0是一個字元,還有字串結束標誌的那個"\0",加起來就是2個位元組。
再比如:
#include <iostream>
using namespace std;
int main()
{
char str[] = "\\0";
cout << strlen(str)<< endl;//2個位元組
cout << sizeof(str) << endl;//3個位元組
system("pause");
return 0;
}
這個程式裡面strlen(str)大小為2個位元組,轉義字元\+\轉義為\,後面還有個數字0,總共是2個位元組;sizeof(str)大小為3個位元組。