前言
??C語言是結(jié)構(gòu)化和模塊化的語言,適合處理較小規(guī)模的程序。對(duì)于復(fù)雜的問題,規(guī)模較大的程序,需要高度的抽象和建模時(shí),C語言則不合適。為了解決軟件危機(jī), 20世紀(jì)80年代, 計(jì)算機(jī)界提出了OOP(object oriented programming:面向?qū)ο?思想,支持面向?qū)ο蟮某绦蛟O(shè)計(jì)語言應(yīng)運(yùn)而生。
??1982年,Bjarne Stroustrup博士在C語言的基礎(chǔ)上引入并擴(kuò)充了面向?qū)ο蟮母拍睿l(fā)明了一種新的程序語言。為了表達(dá)該語言與C語言的淵源關(guān)系,命名為C++。因此:C++是基于C語言而產(chǎn)生的,它既可以進(jìn)行C語言的過程化程序設(shè)計(jì),又可以進(jìn)行以抽象數(shù)據(jù)類型為特點(diǎn)的基于對(duì)象的程序設(shè)計(jì),還可以進(jìn)行面向?qū)ο蟮某绦蛟O(shè)計(jì)。
階段 | 內(nèi)容 |
---|---|
C with classes | 類及派生類、公有和私有成員、類的構(gòu)造和析構(gòu)、友元、內(nèi)聯(lián)函數(shù)、賦值運(yùn)算符重載等 |
C++1.0 | 添加虛函數(shù)概念,函數(shù)和運(yùn)算符重載,引用、常量等 |
C++2.0 | 更加完善支持面向?qū)ο?,新增保護(hù)成員、多重繼承、對(duì)象的初始化、抽象類、靜態(tài)成員以及const成員函數(shù) |
C++3.0 | 進(jìn)一步完善,引入模板,解決多重繼承產(chǎn)生的二義性問題和相應(yīng)構(gòu)造和析構(gòu)的處理 |
C++98 | C++標(biāo)準(zhǔn)第一個(gè)版本,絕大多數(shù)編譯器都支持,得到了國(guó)際標(biāo)準(zhǔn)化組織(ISO)和美國(guó)標(biāo)準(zhǔn)化協(xié)會(huì)認(rèn)可,以模板方式重寫C++標(biāo)準(zhǔn)庫(kù),引入了STL(標(biāo)準(zhǔn)模板庫(kù)) |
C++03 | C++標(biāo)準(zhǔn)第二個(gè)版本,語言特性無大改變,主要:修訂錯(cuò)誤、減少多異性 |
C++05 | C++標(biāo)準(zhǔn)委員會(huì)發(fā)布了一份計(jì)數(shù)報(bào)告(Technical Report,TR1),正式更名C++0x,即:計(jì)劃在本世紀(jì)第一個(gè)10年的某個(gè)時(shí)間發(fā)布 |
C++11 | 增加了許多特性,使得C++更像一種新語言,比如:正則表達(dá)式、基于范圍for循環(huán)、auto關(guān)鍵字、新容器、列表初始化、標(biāo)準(zhǔn)線程庫(kù)等 |
C++14 | 對(duì)C++11的擴(kuò)展,主要是修復(fù)C++11中漏洞以及改進(jìn),比如:泛型的lambda表達(dá)式,auto的返回值類型推導(dǎo),二進(jìn)制字面常量等 |
C++17 | 在C++11上做了一些小幅改進(jìn),增加了19個(gè)新特性,比如:static_assert()的文本信息可選,F(xiàn)old表達(dá)式用于可變的模板,if和switch語句中的初始化器等 |
C++20 | 自C++11以來最大的發(fā)行版,引入了許多新的特性,比如:模塊(Modules)、協(xié)程(Coroutines)、范圍(Ranges)、概念(Constraints)等重大特性,還有對(duì)已有特性的更新:比如Lambda支持模板、范圍for支持初始化等 |
C++23 | 制定ing |
1.C++關(guān)鍵字
??這是C++的所有關(guān)鍵字,每一個(gè)都有特殊含義,此處一個(gè)一個(gè)講麻煩且不易理解,因此等以后遇到了再具體講解。
2.命名空間
??在C/C++中,變量、函數(shù)和后面要學(xué)到的類都是大量存在的,這些變量、函數(shù)和類的名稱將都存在于全局作用域中,可能會(huì)導(dǎo)致很多沖突。使用命名空間的目的是對(duì)標(biāo)識(shí)符的名稱進(jìn)行本地化,以避免命名沖突或名字污染,namespace關(guān)鍵字的出現(xiàn)就是針對(duì)這種問題的。
??舉個(gè)例子,比如在<stdlib.h>中有一個(gè)rand()函數(shù),是用來產(chǎn)生隨機(jī)數(shù)的一個(gè)函數(shù)。
??此時(shí)我們可以看到在還沒有包<stdlib.h>頭文件時(shí)是可以正常運(yùn)行的,那么我們?cè)倏纯窗^文件之后會(huì)發(fā)生什么。
??我們發(fā)現(xiàn)竟然編譯失敗了,這是因?yàn)樵诰幾g時(shí)會(huì)將頭文件內(nèi)的內(nèi)容展開,就會(huì)出現(xiàn)rand()函數(shù)與我們定義的全局變量 rand 一樣的名字,因此就會(huì)產(chǎn)生重定義,因此 namespace 命名空間就隨之產(chǎn)生了,它的作用就是為了避免這種情況的發(fā)生。
??命名空間就是用 namespace 關(guān)鍵字加上任意的名字和花括號(hào)即可。但是大家可以觀察到在輸出的那一行我寫的是WY : : rand,這個(gè)的意思就是輸出 WY 命名空間中的 rand,而不是使用頭文件中的 rand。
??命名空間就像一堵無形的墻,將同名的變量分隔到了不同的空間,你需要哪個(gè)空間的變量,就去哪個(gè)空間去尋找。但是一個(gè)命名空間里又有同名的變量存在呢?C++開創(chuàng)者也早已想到了這個(gè)問題,那就是在命名空間中還可以再開辟命名空間,俗稱套娃。
??命名空間可以定義變量、函數(shù)以及類型。但是需要注意的是在定義結(jié)構(gòu)體類型時(shí),在訪問時(shí)是要在 struct 后面寫的。同時(shí)同一個(gè)工程中是允許出現(xiàn)同名的命名空間的,在編譯時(shí)編譯器會(huì)自動(dòng)將其合并。
int rand = 0;//全局變量
namespace WY
{
int rand = 1;
int Add(int a, int b)
{
return a + b;
}
struct Node
{
int data;
int* next;
};
}
int main()
{
//域作用限定符
printf("%d", rand);
printf("%d", WY::rand);
printf("%d", WY::Add(1,2));
struct WY::Node node;
return 0;
}
??前面都還是用C語音來模擬一些環(huán)境,那么C++本身是什么樣的呢?C++有自己專屬的命名空間和頭文件。
//C++標(biāo)準(zhǔn)庫(kù)的命名空間,將標(biāo)準(zhǔn)庫(kù)的定義與實(shí)現(xiàn)都放入了這個(gè)命名空間中
#include<iostream>
using namespace std;
int main()
{
cout << "Wang You" << endl;
}
??但是這樣的寫法就是將命名空間中的所有內(nèi)容全部展開了,是非常容易造成重定義問題的,比如在你和其他人共同寫一個(gè)項(xiàng)目時(shí),用到了同一個(gè)變量名字,結(jié)果在最后一起運(yùn)行時(shí)都將自己的命名空間全部展開,那么豈不是還會(huì)出現(xiàn)重定義的情況嗎?因此這種寫法在與他人共同合作時(shí)不可取,但是在自己日常練習(xí)中還是可以的,畢竟變量名都是由自己控制的,可以避免這種情況。
??同時(shí)展開也全部展開和部分展開,比如 Add 函數(shù)需要經(jīng)常使用,就可以進(jìn)行部分展開,這樣就不用在每次使用前都加域作用限定符( : : )了。
//全部展開
using namespace WY;
//部分展開
using WY::Add;
3.C++的輸入輸出
??在上面其實(shí)也寫過一次,請(qǐng)看:
#include<iostream>
using namespace std;
int main()
{
int a;
cin >> a;
cout << "Wang You" << endl;
return 0;
}
cout是輸出時(shí)使用的,與C語言中的 printf 作用一致,還有一個(gè)是 cin,與 scanf 作用一致。
與C語言相比需要注意的是:
- 在使用cout標(biāo)準(zhǔn)輸出對(duì)象(控制臺(tái))和cin標(biāo)準(zhǔn)輸入對(duì)象(鍵盤)時(shí),必須包含< iostream >頭文件以及按命名空間使用方法使用std。
- cout和cin是全局的流對(duì)象,endl是特殊的C++符號(hào),表示換行輸出,他們都包含在包含< iostream >頭文件中。
- << 是流插入運(yùn)算符,>> 是流提取運(yùn)算符。
- 使用C++輸入輸出更方便,不需要像printf/scanf輸入輸出時(shí)那樣,需要手動(dòng)控制格式。C++的輸入輸出可以自動(dòng)識(shí)別變量類型。
- 實(shí)際上cout和cin分別是ostream和istream類型的對(duì)象,>>和<<也涉及運(yùn)算符重載等知識(shí),這些知識(shí)后續(xù)將會(huì)進(jìn)行講解,所以這里只是簡(jiǎn)單學(xué)習(xí)他們的使用。
using namespace std展開,標(biāo)準(zhǔn)庫(kù)就全部暴露出來了,如果我們定義跟庫(kù)重名的類型/對(duì)象/函數(shù),就會(huì)存在沖突問題。該問題在日常練習(xí)中很少出現(xiàn),但是項(xiàng)目開發(fā)中代碼較多、規(guī)模大,就很容易出現(xiàn)。所以建議在項(xiàng)目開發(fā)中使用,像std::cout這樣使用時(shí)指定命名空間 + using std::cout展開常用的庫(kù)對(duì)象/類型等方式。
4.缺省參數(shù)
??缺省參數(shù)是聲明或定義函數(shù)時(shí)為函數(shù)的參數(shù)指定一個(gè)缺省值。在調(diào)用該函數(shù)時(shí),如果沒有指定實(shí)參則采用該形參的缺省值,否則使用指定的實(shí)參。
??一般在調(diào)用一個(gè)函數(shù)時(shí),我們都會(huì)對(duì)它進(jìn)行傳參,缺省參數(shù)的意義在于,當(dāng)你沒有給函數(shù)傳參時(shí),它會(huì)自動(dòng)默認(rèn)傳入你設(shè)置的缺省參數(shù)(int a = 0)。
4.1 全缺省
void fun(int a = 10, int b = 20, int c = 30)
{
cout << a << " ";
cout << b << " ";
cout << c << " ";
cout << endl;
}
??由此我們也可以看出缺省參數(shù)必須從右往左給,因此我們給的參數(shù)是從左往右的,左邊的數(shù)據(jù)可以我們顯示傳參,右邊的可以使用缺省參數(shù)。
4.2 半缺省
??那我們就再來看看半缺省,顧名思義就是只有一部分有缺省值。
void fun(int a,int b,int c = 30)
{
cout << a << endl;
cout << b << endl;
cout << c << endl;
}
注意:
- 半缺省參數(shù)必須從右往左依次來給出,不能間隔著給
- 缺省參數(shù)不能在函數(shù)聲明和定義中同時(shí)出現(xiàn)
- 缺省值必須是常量或者全局變量
- C語言不支持(編譯器不支持)
??不同聲明和定義中同時(shí)出現(xiàn)是因?yàn)槿绻x中給的默認(rèn)參數(shù)與聲明中給的默認(rèn)參數(shù)不同的話,編譯器是無法分別的,因此統(tǒng)一都是在定義是再給默認(rèn)參數(shù)(缺省參數(shù))。
5.函數(shù)重載
??函數(shù)重載是函數(shù)的一種特殊情況,C++允許在同一作用域中聲明幾個(gè)功能類似的同名函數(shù),這些同名函數(shù)的形參列表(參數(shù)個(gè)數(shù)或類型或類型順序)不同,常用來處理實(shí)現(xiàn)功能類似數(shù)據(jù)類型不同的問題。
??系統(tǒng)會(huì)根據(jù)它們的參數(shù)類型自動(dòng)匹配最相符的函數(shù)進(jìn)行調(diào)用,即使函數(shù)名相同也是可以的。
但是必須要滿足形參列表(參數(shù)個(gè)數(shù)或類型或類型順序)不同,這三個(gè)條件中的一個(gè)才可以。
那么編譯器是如何區(qū)分的呢?
??其實(shí)在編輯器內(nèi)部調(diào)用這個(gè)函數(shù)時(shí),是通過它的函數(shù)名加地址去尋找它的,每一個(gè)函數(shù)在鏈接時(shí)在內(nèi)部的函數(shù)名,編譯器都會(huì)在你書寫的函數(shù)名(fun)的基礎(chǔ)上再根據(jù)它的參數(shù)來進(jìn)行添加一些符號(hào)來修飾它。比如在VS2019中,在鏈接過程中這個(gè)兩個(gè)函數(shù)的函數(shù)名就是如圖這樣的,在后面一個(gè)是 HN,一個(gè)是NH。因此編譯器才會(huì)準(zhǔn)確的區(qū)分它們。
??而在C語言中,對(duì)于內(nèi)部的函數(shù)名修飾是一樣,所以編譯器是無法區(qū)別的。 通過這里就理解了C語言為什么沒辦法支持重載了,因?yàn)橥瘮?shù)沒辦法區(qū)分。而C++是通過函數(shù)修飾規(guī)則來區(qū)分,只要參數(shù)不同,修飾出來的名字就不一樣,就支持了重載。
如果兩個(gè)函數(shù)函數(shù)名和參數(shù)是一樣的,返回值不同是不構(gòu)成重載的,因?yàn)檎{(diào)用時(shí)編譯器沒辦法區(qū)分。
6. 引用
??引用不是新定義一個(gè)變量,而是給已存在的變量取了一個(gè)別名(也就是外號(hào)),編譯器不會(huì)為引用變量開辟內(nèi)存空間,它和它引用的變量共用同一塊內(nèi)存空間。
??就比如一塊空間名字是 a,你又給它起了個(gè)外號(hào)叫 b,雖然名字不一樣,但是是同一塊空間。
??看似兩個(gè)名字,實(shí)則是同一塊空間,一榮俱榮一損俱損,你是我,我是你的關(guān)系。需要注意的是引用類型必須和引用實(shí)體是同種類型的。
6.1 引用的特性
- 引用在定義時(shí)必須初始化
- 一個(gè)變量可以有多個(gè)引用
- 引用一旦引用一個(gè)實(shí)體,再不能引用其他實(shí)體
int main()
{
int a = 10;
int& b;
//正確寫法:int& b = a;
return 0;
}
??這樣寫是不行的,因?yàn)槟闶窃诮o已存在變量取別名,不初始化是無法作為別人的外號(hào)存在的。
int main()
{
int a = 10;
int& ra = a;
int& raa = a;
return 0;
}
??這樣寫是可以的,一個(gè)變量可以有很多個(gè)外號(hào)。但是一個(gè)外號(hào)只能給一個(gè)人用,誰都能用豈不是就亂套了,所以引用一旦引用一個(gè)實(shí)體,再不能引用其他實(shí)體。
??對(duì)于 const 修飾的變量意味著只能讀不能寫,因此在為 const 變量取別名時(shí)也要加 const,不能出現(xiàn)本來無法修改,你加個(gè)外號(hào)就能修改的情況吧。
void test()
{
const int a = 10;
int& ra = a; // 該語句編譯時(shí)會(huì)出錯(cuò),a為常量
//正確寫法:const int& ra = a;
const int& ra = a;
int& b = 10; // 該語句編譯時(shí)會(huì)出錯(cuò),b為常量
//正確寫法:const int& b = 10;
const int& b = 10;
double d = 12.34;
int& rd = d; // 該語句編譯時(shí)會(huì)出錯(cuò),類型不同
//正確寫法:const int& rd = d;
}
??而對(duì)于最后一個(gè)類型不一樣的,為什么加了 const 就可以了呢?這是因?yàn)樵谶M(jìn)行隱式類型轉(zhuǎn)化時(shí),會(huì)先生成一個(gè)臨時(shí)的 int 常量,先將 d 的值由 double 類型轉(zhuǎn)為 int 類型賦給臨時(shí)變量,然后再為臨時(shí)變量取的別名。因?yàn)槭浅A繜o法修改,所以在取別名時(shí)也需要加 const。
6.2 引用的使用場(chǎng)景
??作為函數(shù)的參數(shù),這樣不需要指針就可以交換兩個(gè)變量的值。
void Swap(int& left, int& right)
{
int temp = left;
left = right;
right = temp;
}
??作返回值。引用返回效率會(huì)更快一點(diǎn),因?yàn)橹苯臃祷氐氖亲兞康膭e名。在正常的函數(shù)中,因此函數(shù)在調(diào)用完就被銷毀了,因此它是將返回值先給了一個(gè)臨時(shí)變量,由臨時(shí)變量返回給主函數(shù)。因此需要注意的是,引用返回時(shí)返回的必須是靜態(tài)變量或者是堆上的變量。
int& Count()
{
static int n = 0;
n++;
// ...
return n;
}
以值作為參數(shù)或者返回值類型,在傳參和返回期間,函數(shù)不會(huì)直接傳遞實(shí)參或者將變量本身直接返回,而是傳遞實(shí)參或者返回變量的一份臨時(shí)的拷貝,因此用值作為參數(shù)或者返回值類型,效率是非常低下的,尤其是當(dāng)參數(shù)或者返回值類型非常大時(shí),效率就更低。
6.3 引用和指針
??指針和引用使用起來不一樣,但在底層實(shí)現(xiàn)是其實(shí)是一樣的,我們來看這樣一段代碼。
#include<iostream>
using namespace std;
int main()
{
int a = 10;
int* pa = &a;
int& ra = a;
++(*pa);
++ra;
return 0;
}
??雖然對(duì)于匯編不太理解,但是不妨礙我們可以看出對(duì)于指針運(yùn)算和引用運(yùn)算,編譯器在底層用匯編實(shí)現(xiàn)是一致的,因此我們就可以知道引用實(shí)際上也是通過指針來實(shí)現(xiàn)的。
- 引用概念上定義一個(gè)變量的別名,指針存儲(chǔ)一個(gè)變量地址。
- 引用在定義時(shí)必須初始化,指針沒有要求
- 引用在初始化時(shí)引用一個(gè)實(shí)體后,就不能再引用其他實(shí)體,而指針可以在任何時(shí)候指向任何
一個(gè)同類型實(shí)體 - 沒有NULL引用,但有NULL指針
- 在sizeof中含義不同:引用結(jié)果為引用類型的大小,但指針始終是地址空間所占字節(jié)個(gè)數(shù)(32
位平臺(tái)下占4個(gè)字節(jié)) - 引用自加即引用的實(shí)體增加1,指針自加即指針向后偏移一個(gè)類型的大小
- 有多級(jí)指針,但是沒有多級(jí)引用
- 訪問實(shí)體方式不同,指針需要顯式解引用,引用編譯器自己處理
- 引用比指針使用起來相對(duì)更安全
7.內(nèi)聯(lián)函數(shù)
??以inline修飾的函數(shù)叫做內(nèi)聯(lián)函數(shù),編譯時(shí)C++編譯器會(huì)在調(diào)用內(nèi)聯(lián)函數(shù)的地方展開,沒有函數(shù)調(diào)用建立棧幀的開銷,內(nèi)聯(lián)函數(shù)提升了程序運(yùn)行的效率。
inline void Fun(int& a, int& b)
{
int tmp = a;
a = b;
b = tmp;
}
int main()
{
int a = 10, b = 20;
Fun(a, b);
return 0;
}
int main()
{
int a = 10, b = 20;
int tmp = a;
a = b;
b = tmp;
return 0;
}
??內(nèi)聯(lián)函數(shù)的作用就是將上面的代碼轉(zhuǎn)化為下面的代碼,也就是不去調(diào)用函數(shù)了,而是將函數(shù)代碼在原地展開。那大家有沒有發(fā)現(xiàn)這和C語言中的什么比較相似呢?沒錯(cuò),就是宏。
#define Add(a,b) ((a) + (b))
??這是用宏實(shí)現(xiàn)了一個(gè)加法函數(shù),我們知道宏就是直接在相應(yīng)的位置替換,但是我們知道操作符是有優(yōu)先級(jí)的,直接替換過去是有可能發(fā)生操作符優(yōu)先級(jí)不正確導(dǎo)致出錯(cuò)的。
宏的缺點(diǎn):
- 不方便調(diào)試宏。(因?yàn)轭A(yù)編譯階段進(jìn)行了替換)
- 導(dǎo)致代碼可讀性差,可維護(hù)性差,容易誤用。
- 沒有類型安全的檢查 。
宏的優(yōu)點(diǎn)
- 沒有類型的嚴(yán)格控制。
- 針對(duì)頻繁調(diào)用小函數(shù),不需要建立棧幀,提高性能。
??因此為了避免這種錯(cuò)誤,C++新增了內(nèi)聯(lián)函數(shù)inline來解決這個(gè)問題。不需要大家刻意的寫很多括號(hào)來保證操作符的正確使用順序。
7.1 特性
- inline是一種以空間換時(shí)間的做法,如果編譯器將函數(shù)當(dāng)成內(nèi)聯(lián)函數(shù)處理,在編譯階段,會(huì)用函數(shù)體替換函數(shù)調(diào)用,缺陷:可能會(huì)使目標(biāo)文件變大,優(yōu)勢(shì):少了調(diào)用開銷,提高程序運(yùn)行效率。
- inline對(duì)于編譯器而言只是一個(gè)建議,不同編譯器關(guān)于inline實(shí)現(xiàn)機(jī)制可能不同,一般建議:將函數(shù)規(guī)模較小(即函數(shù)不是很長(zhǎng),具體沒有準(zhǔn)確的說法,取決于編譯器內(nèi)部實(shí)現(xiàn))、不是遞歸、且頻繁調(diào)用的函數(shù)采用inline修飾,否則編譯器會(huì)忽略inline特性。《C++prime》第五版關(guān)于inline的建議:內(nèi)聯(lián)只是向編譯器發(fā)出一個(gè)請(qǐng)求,編譯器可以忽略這個(gè)請(qǐng)求。
- inline不建議聲明和定義分離,分離會(huì)導(dǎo)致鏈接錯(cuò)誤。因?yàn)閕nline被展開,就沒有函數(shù)地址了,鏈接就會(huì)找不到。
8.auto關(guān)鍵字
??在早期C/C++中auto的含義是:使用auto修飾的變量,是具有自動(dòng)存儲(chǔ)器的局部變量,但遺憾的是一直沒有人去使用它,大家可思考下為什么?
??C++11中,標(biāo)準(zhǔn)委員會(huì)賦予了auto全新的含義即:auto不再是一個(gè)存儲(chǔ)類型指示符,而是作為一個(gè)新的類型指示符來指示編譯器,auto聲明的變量必須由編譯器在編譯時(shí)期推導(dǎo)而得。
??也就是 auto 會(huì)自動(dòng)推到出變量的類型,在對(duì)于一些變量類型比較長(zhǎng)是會(huì)方便的,后續(xù)就會(huì)遇到。
typeid().name() 的作用就是識(shí)別一個(gè)變量的類型。
?
使用auto定義變量時(shí)必須對(duì)其進(jìn)行初始化,在編譯階段編譯器需要根據(jù)初始化表達(dá)式來推導(dǎo)auto的實(shí)際類型。因此auto并非是一種“類型”的聲明,而是一個(gè)類型聲明時(shí)的“占位符”,編譯器在編譯期會(huì)將auto替換為變量實(shí)際的類型。
8.1 注意事項(xiàng)
??用auto聲明指針類型時(shí),用auto和auto*沒有任何區(qū)別,但用auto聲明引用類型時(shí)則必須加&。
??當(dāng)在同一行聲明多個(gè)變量時(shí),這些變量必須是相同的類型,否則編譯器將會(huì)報(bào)錯(cuò),因?yàn)榫幾g器實(shí)際只對(duì)第一個(gè)類型進(jìn)行推導(dǎo),然后用推導(dǎo)出來的類型定義其他變量。
void test_auto()
{
auto a = 1, b = 2;
auto c = 3, d = 4.0;
// 該行代碼會(huì)編譯失敗,因?yàn)閏和d的初始化表達(dá)式類型不同
}
- auto不能作為函數(shù)的參數(shù)。
- auto不能直接用來聲明數(shù)組。
- 為了避免與C++98中的auto發(fā)生混淆,C++11只保留了auto作為類型指示符的用法。
- auto在實(shí)際中最常見的優(yōu)勢(shì)用法就是跟以后會(huì)講到的C++11提供的新式for循環(huán),還有l(wèi)ambda表達(dá)式等進(jìn)行配合使用。
9. 基于范圍的for循環(huán)
??我們通常遍歷一個(gè)數(shù)組是這樣的:
int main()
{
int a[5] = { 0,1,2,3,4 };
for(int i = 0; i < 5; i++)
{
cout << a[i] << " ";
}
return 0;
}
??對(duì)于一個(gè)有范圍的集合而言,由程序員來說明循環(huán)的范圍是多余的,有時(shí)候還會(huì)容易犯錯(cuò)誤。因此C++11中引入了基于范圍的for循環(huán)。for循環(huán)后的括號(hào)由號(hào)“ :”分為兩部分:第一部分是范圍內(nèi)用于迭代的變量,第二部分則表示被迭代的范圍。
??實(shí)際過程是將 arr 中的一個(gè)數(shù)拿出來放到 e 中,進(jìn)行輸出,再繼續(xù)拿第二個(gè)數(shù)……
9.1 使用條件
- for循環(huán)迭代的范圍必須是確定的
??對(duì)于數(shù)組而言,就是數(shù)組中第一個(gè)元素和最后一個(gè)元素的范圍;對(duì)于類而言,應(yīng)該提供begin和end的方法,begin和end就是for循環(huán)迭代的范圍。
void test_for(int arr[])
{
for(auto& e : arr)
cout<< e <<endl;
}
??這樣寫就是有問題的,因?yàn)?for 的范圍不確定。
2. 迭代的對(duì)象要實(shí)現(xiàn)++和==的操作,這里針對(duì)的是類。
10.指針控制nullptr
??在良好的C/C++編程習(xí)慣中,聲明一個(gè)變量時(shí)最好給該變量一個(gè)合適的初始值,否則可能會(huì)出現(xiàn)不可預(yù)料的錯(cuò)誤,比如未初始化的指針。如果一個(gè)指針沒有合法的指向,我們基本都是按照如下方式對(duì)其進(jìn)行初始化:
void test_ptr()
{
int* p1 = NULL;
int* p2 = 0;
//……
}
??NULL實(shí)際是一個(gè)宏,在傳統(tǒng)的C頭文件(stddef.h)中,可以看到如下代碼:
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
??可以看到,NULL可能被定義為字面常量0,或者被定義為無類型指針(void*)的常量。不論采取何種定義,在使用空值的指針時(shí)都不可避免的會(huì)遇到一些麻煩,比如:
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;
}
??程序本意是想通過f(NULL)調(diào)用指針版本的f(int*)函數(shù),但是由于NULL被定義成0,因此與程序的初衷相悖。
??在C++98中,字面常量0既可以是一個(gè)整形數(shù)字,也可以是無類型的指針(void*)常量,但是編譯器默認(rèn)情況下將其看成是一個(gè)整形常量,如果要將其按照指針方式來使用,必須對(duì)其進(jìn)行強(qiáng)轉(zhuǎn)(void *)0。
??因此在C++中,區(qū)分了這種情況,讓 nullptr 專門代指了指針。文章來源:http://www.zghlxwxcb.cn/news/detail-649995.html
10.1 注意事項(xiàng)
- 在使用nullptr表示指針空值時(shí),不需要包含頭文件,因?yàn)閚ullptr是C++11作為新關(guān)鍵字引入的。
- 在C++11中,sizeof(nullptr) 與 sizeof((void*)0)所占的字節(jié)數(shù)相同。
- 為了提高代碼的健壯性,在后續(xù)表示指針空值時(shí)建議最好使用nullptr。
11.總結(jié)
??在初學(xué)C++時(shí),需要記憶的地方有很多,難度不大但細(xì)節(jié)較多,建議大家整理一些筆記來幫助日后的復(fù)習(xí)。
??此篇內(nèi)容字?jǐn)?shù)尚可,初學(xué)C++還是略感疲憊(嘆氣),不過與之前相比好多了(愉悅)。如果大家發(fā)現(xiàn)有什么錯(cuò)誤的地方,可以私信或者評(píng)論區(qū)指出喔(虛心請(qǐng)教,渴望大佬幫助)。我會(huì)繼續(xù)深入學(xué)習(xí)C++,希望能與大家共同進(jìn)步,那么本期就到此結(jié)束,讓我們下期再見!!覺得不錯(cuò)可以點(diǎn)個(gè)贊以示鼓勵(lì)喔!!文章來源地址http://www.zghlxwxcb.cn/news/detail-649995.html
到了這里,關(guān)于C++入門基礎(chǔ)(萬字詳解?。。。┑奈恼戮徒榻B完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!