目錄
一. 前言
二. auto關(guān)鍵字
2.1 auto的引入
2.2 auto簡介
2.3 auto的使用細則
2.4?auto不能推導的場景
三. 基于范圍的for循環(huán)(C++11)
3.1 范圍for的語法
3.2 范圍for的原理
3.3 范圍for的使用條件
四. 指針空值nullptr(C++11)
一. 前言
? ? ? ? 上期我們介紹了c++新增的兩個重要語法:引用和內(nèi)聯(lián)函數(shù),今天我們帶來的內(nèi)容是auto關(guān)鍵字、范圍for以及nullptr指針,本期也是初識C++的最后一期。上期回顧:
【C++深入淺出】初識C++中篇(引用、內(nèi)聯(lián)函數(shù))http://t.csdn.cn/LCvY0????????話不多說,直接上菜!?。?/span>
二. auto關(guān)鍵字
2.1 auto的引入
? ? ? ? 在我們寫代碼的過程中,可曾發(fā)現(xiàn),隨著程序越來越復雜,程序中用到的類型也越來越復雜,包括但不限于以下兩點:1.?類型難于拼寫、2.?含義不明確導致容易出錯。例如:
#include <string>
#include <map>
int main()
{
std::map<std::string, std::string> m{ { "apple", "蘋果" }, { "orange","橙子" },{ "pear","梨" } };
std::map<std::string, std::string>::iterator it = m.begin();
while (it != m.end())
{
//....
}
return 0;
}
你沒有看錯,上面的std::map<std::string, std::string>::iterator?其實是一個類型,是不是非常嚇人
。像這種長類型特別容易寫錯,有的人可能已經(jīng)想到了解決方案:可以通過typedef給類型取別名。是的,這無疑也是種好方法,但使用typedef前我們必須先知道類型,有時候這并不容易做到。那怎么辦呢?不急,C++11中的auto關(guān)鍵字就是為了解決這個問題。
2.2 auto簡介
????????在早期C/C++中使用auto修飾的變量,是具有自動存儲器的局部變量,但由于用處不大,一直沒有人去使用它。
? ? ? ? 而在C++11中,C++標準委員會賦予了auto全新的含義,即auto不再是一個存儲類型指示符,而是作為一個新的類型指示符來指示編譯器,使用auto聲明的變量類型編譯器會在編譯時期自動推導而得。
????????為了避免與C++98中的auto發(fā)生混淆,C++11只保留了auto作為類型指示符的用法。
#include<string>
int TestAuto()
{
return 10;
}
int main()
{
int a = 10;
string s;
auto b = a;
auto c = 'a';
auto d = TestAuto();
auto e = s.begin();
cout << typeid(b).name() << endl; //typeid類似于sizeof一樣,是一個操作符,其可以用來獲取變量的類型。
cout << typeid(c).name() << endl;
cout << typeid(d).name() << endl;
cout << typeid(e).name() << endl;
//auto e; 無法通過編譯,使用auto定義變量時必須對其進行初始化
return 0;
}
輸出結(jié)果如下?
可以看出,編譯器自動幫我們將類型推導出來了,是不是非常方便
2.3 auto的使用細則
? ? ? ? 學會了auto的基本使用,接下來就是避坑時間惹?
? ? ? ? 1. auto定義變量時必須對其進行初始化
? ? ? ? ?auto并非是一種“類型”的聲明,而是一個類型聲明時的“占位符”。在編譯階段編譯器需要根據(jù)初始化表達式來推導auto的實際類型,然后將auto替換為變量實際的類型。
int main()
{
int val = 10;
auto a; //錯誤寫法,編譯時會報錯
auto b = val; //正確寫法,定義時進行初始化,編譯器才能進行推導然后將auto替換
return 0;
}
? ? ? ? 2.?auto和指針和引用
? ? ? ? 用auto聲明指針類型時,用auto和auto*沒有任何區(qū)別,但用auto聲明引用類型時則必須
加&。
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;
*a = 20;
*b = 30;
c = 40;
return 0;
}
?????????3. 在同一行定義多個變量
? ? ? ? ?當在同一行聲明多個變量時,這些變量必須類型相同,否則編譯器將會報錯,因為編譯
器實際只對第一個類型進行推導,然后將auto進行替換,最后用替換后的類型定義其他變量。
int main()
{
auto a = 1, b = 2; // 正確寫法
auto c = 3, d = 4.0; // 編譯失敗,因為c和d的初始化表達式類型不同
return 0;
}
2.4?auto不能推導的場景
? ? ? ? 1、auto不能作為函數(shù)的參數(shù)
// 此處代碼編譯失敗,auto不能作為形參類型,
// 原因:函數(shù)調(diào)用傳參發(fā)生在運行階段,故在編譯階段編譯器無法對a的實際類型進行推導
void TestAuto(auto a)
{}
? ? ? ? 2.、auto不能直接用來聲明數(shù)組
int main()
{
int a[] = { 1,2,3 };
auto b[] = { 4,5,6 };
return 0;
}
三. 基于范圍的for循環(huán)(C++11)
3.1 范圍for的語法
????????在C++98中如果要遍歷一個數(shù)組,可以按照以下方式進行:
int main()
{
int array[] = { 1, 2, 3, 4, 5 };
for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
{
array[i] *= 2; //利用下標訪問
}
cout << endl;
for (int* p = array; p < array + sizeof(array) / sizeof(array[0]); ++p)
{
cout << *p << ' '; //利用指針訪問
}
cout << endl;
}
? ? ? ? 但是對于array這個有范圍的集合而言,由程序員來說明循環(huán)的范圍顯然顯得有點多余,有時候還會容易犯錯誤產(chǎn)生越界。因此C++11中引入了基于范圍的for循環(huán)。for循環(huán)后的括號由冒號分為兩部分:第一部分是范圍內(nèi)迭代取得的變量,第二部分則表示進行迭代的集合對象。上述代碼使用范圍for改寫如下
int main()
{
int array[] = { 1, 2, 3, 4, 5 };
for (auto& e : array) //依次取數(shù)組的數(shù)據(jù)給引用變量e,自動判斷結(jié)束,自動迭代 + 1
{
e *= 2;
}
for (auto e : array) //依次取數(shù)組的數(shù)據(jù)賦值給變量e,自動判斷結(jié)束,自動迭代 + 1
{
cout << e << " ";
}
return 0;
}
可以看到,我們使用到了之前學的auto關(guān)鍵字,利用auto的自動類型推導,我們無需顯式地寫出e的類型,使得范圍for的使用更加簡潔方便,這也是auto的常見優(yōu)勢用法之一。
在第一個范圍for中,由于e是引用變量,因此e表示的是數(shù)組每個元素的別名,對e進行修改就是對數(shù)組元素進行修改。
而在第二個范圍for中,e是普通變量,表示的是數(shù)組每個元素的拷貝,對e進行修改對數(shù)組元素沒有影響。
注意:與普通循環(huán)類似,可以用continue來結(jié)束本次循環(huán),也可以用break來跳出整個循環(huán)。
3.2 范圍for的原理
? ? ? ? 可能會有的小伙伴會好奇:范圍for這個東西這么智能,既能自動迭代,還能自動判斷結(jié)束,那么它的原理究竟是什么呢?
? ? ? ? ?實際上,并沒有想象中的那么復雜。范圍for的底層原理實際上就是迭代器遍歷,編譯器在編譯時會自動將范圍for的代碼替換為迭代器遍歷相關(guān)代碼。迭代器的知識我們后續(xù)會介紹,這里大家將其理解為指針即可。
? ? ? ? 下面是一段vector容器的遍歷代碼:
int main()
{
vector<int> v;
for (int i = 1; i <= 5; i++) //插入1-5的數(shù)據(jù)
{
v.push_back(i);
}
for (auto e : v) //范圍for遍歷
{
cout << e << ' ';
}
cout << endl;
return 0;
}
? ? ? ?編譯器編譯時范圍for會替換成類似于如下的代碼:
int main()
{
vector<int> v;
for (int i = 1; i <= 5; i++) //插入1-5的數(shù)據(jù)
{
v.push_back(i);
}
vector<int>::iterator it = v.begin();
//auto it = v.begin(); //迭代器的類型較長,也可以使用auto自動推導
while (it != v.end()) //迭代器遍歷,這里將it當做指針理解即可
{
cout << *it << ' ';
it++;
}
cout << endl;
return 0;
}
結(jié)論:范圍for其實就是編譯器進行了替換,本質(zhì)上還是迭代器的遍歷。
3.3 范圍for的使用條件
????????1、for循環(huán)迭代的范圍必須是確定的
????????對于數(shù)組而言,for循環(huán)迭代的范圍就是從數(shù)組中第一個元素到最后一個元素;對于類而言,應(yīng)該提供begin和end的方法,begin和end就是for循環(huán)迭代的范圍。
void putArray(int array[])
{
//數(shù)組傳參發(fā)生降維,array是個指針,指向數(shù)組首元素
for (auto e : array) //由于array只是個int*指針,我們無法確定迭代的范圍,故這里的范圍for會報錯
{
cout << e << ' ';
}
}
int main()
{
vector<int> v;
for (int i = 1; i <= 5; i++) //插入1-5的數(shù)據(jù)
{
v.push_back(i);
}
for (auto e : v) //范圍for遍歷vector容器類。范圍:v.begin()~v.end()
{
cout << e << ' ';
}
cout << endl;
int array[5] = { 1,2,3,4,5 };
for (auto e : v) //范圍for遍歷array數(shù)組。范圍:從第一個元素的下標0到最后一個元素的下標4
{
cout << e << ' ';
}
putArray(array);
return 0;
}
? ? ? ? 2、迭代的對象要實現(xiàn)++和==的操作
? ? ? ? 上面我們看到范圍for替換為迭代器遍歷的代碼中,使用迭代器it進行遍歷時需要用到++和==的操作,顧迭代器需要支持++和==的操作。(目前不清楚的了解一下即可,等到我們講解迭代器時再深入討論)
四. 指針空值nullptr(C++11)
? ? ? ? 在C語言中,我們對指針進行初始化時,經(jīng)常會用NULL空指針進行初始化,如下:
int main()
{
int* p1 = NULL;
return 0;
}
?????????實際上,NULL是一個宏,在傳統(tǒng)的C頭文件(stddef.h)中,可以看到如下代碼:
????????可以看到,NULL在C++中被定義為字面常量0,在C語言中被定義為無類型指針(void*)常量。而字面常量0在編譯器看來默認是整形常量,這就會導致出現(xiàn)一些不可預料的錯誤,例如:
void f(int)
{
cout << "f(int)" << endl;
}
void f(int*)
{
cout << "f(int*)" << endl;
}
int main()
{
f(0);
f(NULL);
f((int*)NULL);
return 0;
}
? ? ? ? 上述代碼在C++中的結(jié)果如下所示
本意我們是想通過f(NULL)調(diào)用指針版本的f(int*)函數(shù),但是由于NULL在C++中被定義成字面常量0,因此調(diào)用了更符合的f(int)函數(shù),與程序的初衷相悖。如果我們執(zhí)意要調(diào)用f(int*)函數(shù),只能將NULL強制類型轉(zhuǎn)換為int*,再進行調(diào)用即可,但這樣會顯得非常奇怪
?
? ? ? ? 出于以上原因,C++11新增了一個關(guān)鍵字nullptr用來表示指針空值,如下:
void f(int)
{
cout << "f(int)" << endl;
}
void f(int*)
{
cout << "f(int*)" << endl;
}
int main()
{
f(0);
f(nullptr); //nullptr表示空指針
return 0;
}
? ? ? ? 此時代碼的結(jié)果就符合我們初衷了:
關(guān)于nullptr的幾點說明與建議?
- 在使用nullptr表示指針空值時,不需要包含頭文件,因為nullptr是C++11作為新關(guān)鍵字引入的。
- 在C++11中,sizeof(nullptr) 與 sizeof((void*)0)所占的字節(jié)數(shù)相同。
- 為了提高代碼的健壯性,在C++中表示指針空值時最好使用nullptr。
以上,就是本期的全部內(nèi)容啦??文章來源:http://www.zghlxwxcb.cn/news/detail-678333.html
制作不易,能否點個贊再走呢??文章來源地址http://www.zghlxwxcb.cn/news/detail-678333.html
到了這里,關(guān)于【C++深入淺出】初識C++下篇(auto關(guān)鍵字、范圍for、nullptr指針)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!