上次介紹了:開啟C++之旅(上):探索命名空間與函數(shù)特性(缺省參數(shù)和函數(shù)重載)
今天就接著進行c++入門的知識講解
1.引用
1.1引用概念
引用不是新定義一個變量,而是給已存在變量取了一個別名,編譯器不會為引用變量開辟內(nèi)存空間,它和它引用的變量共用同一塊內(nèi)存空間。通過引用,你可以使用一個變量的多個名稱來訪問和修改它的值
定義形式:
類型& 引用變量名 = 引用實體(這里&就不是c中大家熟知的取地址了)
注意:引用類型必須和引用實體是同種類型的
int main()
{
int a = 10;
int& b = a;
cout << a << endl;
b = 1;
cout << a << endl;
return 0;
}
二者也是共用一塊內(nèi)存空間
1.2引用特性
- 引用在定義時必須初始化
- 一個變量可以有多個引用(可以起多個別名)
- 引用一旦引用一個實體,再不能引用其他實體(不能改變指向)
int main()
{
int a = 0;
int& b = a;
int c = 10;
b = c;//那這個到底是 c變成d的別名?還是d賦值給c?
return 0;
}
因為引用不能改變指向,這個是賦值
1.3常引用
int main()
{
const int a = 10;
int& ra = a;//這樣編譯器會報錯
return 0;
}
實際上這樣會擴大權(quán)限:本身用
const
修飾后,不能改變a
的值了,但是如果引用后就能利用引用改變。這樣擴大了權(quán)限這時使用常引用
int main() { const int a = 10; const int& ra = a;//這樣才對,沒有擴大權(quán)限 return 0; }
其他情況
- 對非常量定義常引用
int main()
{
int a = 10;
const int& ra = a;//這樣也可以,不會報錯
return 0;
}
權(quán)限縮小是沒問題的
- 隱式類型轉(zhuǎn)換,截斷,強制類型轉(zhuǎn)換
在 C++ 中,隱式類型轉(zhuǎn)換、截斷和強制類型轉(zhuǎn)換都可能導(dǎo)致臨時變量的創(chuàng)建,并且這些臨時變量通常具有常量性質(zhì),那就需要用常引用
加了
const
就好了
在 C++ 中進行類型轉(zhuǎn)換時,通常會創(chuàng)建一個臨時變量來存儲轉(zhuǎn)換后的結(jié)果。這個臨時變量是一個匿名對象,它存儲了轉(zhuǎn)換后的值,但并不會影響原始變量的值。這也是為什么對
a
進行類型轉(zhuǎn)換后賦值,但是a
不發(fā)生變化
1.4引用使用場景
1.4.1做參數(shù)
在函數(shù)中使用引用作為參數(shù),可以讓你直接操作傳遞給函數(shù)的變量,而不是對其進行復(fù)制。這樣可以避免復(fù)制大型對象,提高效率,同時允許函數(shù)修改傳遞的變量值(可以簡單理解為:我們把別名傳了過來,當(dāng)然能通過別名來改變本身)
void Swap(int& a, int& b)//交換兩個整形
{
int temp = b;
b = a;
a = temp;
}
1.4.2做返回值
在 C++ 中,函數(shù)可以返回引用,以避免在返回函數(shù)結(jié)果時產(chǎn)生拷貝。直接返回傳遞的變量的引用,允許你對該變量進行操作。然而,使用引用作為返回值需要小心,確保引用所指向的變量在函數(shù)返回后仍然有效
使用引用作為返回值的語法是在函數(shù)聲明或定義中將函數(shù)返回類型聲明為引用類型。
但是注意
- 不能返回局部變量的引用
int& Add(int a, int b)
{
int c = a + b;
return c;//返回了局部變量的引用
}
int main()
{
//有問題的代碼,這里不能用引用返回,否則為一個不確定的值
int& ret = Add(1, 2);
cout << "Add(1, 2) is :" << ret << endl;
Add(3, 4);
cout << "Add(1, 2) is :" << ret << endl;
return 0;
}
- 在這里返回值是不是隨機值,取決于是否清理棧幀?。。。]有清理就是原值,清理了就是隨機的了)由于我是用VS編譯器,VS出棧沒有清理棧幀,所以導(dǎo)致這里打印出的是需要的到的值
-
ret
始終是函數(shù)調(diào)用時使用的空間里變量c
的別名
出了函數(shù)作用域,返回對象(局部變量)就銷毀了,不能用引用返回,否則結(jié)果是不確定
最好返回指向全局變量、靜態(tài)變量、或動態(tài)分配內(nèi)存的引用,確保引用在函數(shù)返回后仍然有效
- 如果使用static來解決上述問題,一定把靜態(tài)變量初始化和賦值分開
int& Add(int a, int b)
{
static int c = a + b;
return c;
}
int main()
{
int& ret = Add(1, 2);
cout << "Add(1, 2) is :" << ret << endl;
Add(3, 4);
cout << "Add(3, 4) is :" << ret << endl;
return 0;
}
仍然有問題,因為
static
關(guān)鍵字用在局部變量上表示該變量在程序運行期間只初始化一次,后續(xù)再次調(diào)用時,不走那條語句了,直接return c
int& Add(int a, int b)
{
static int c ;
c = a + b;//分開就行了
return c;
}
1.5引用與指針的區(qū)別
相信大家一開始都會抱有疑問,引用現(xiàn)在能做的指針不也都可以嗎? 還有那個別名的底層是什么,怎么理解?
雖然在底層里,創(chuàng)建的引用變量實際是有空間的,可以通過匯編來觀察引用是按照指針方式來實現(xiàn)的
但是
在語法上:我們只是給那個空間取了一個別名,沒有開辟空間
int main()
{
char a = '1';
char& b = a;//如果開了空間,大小是4,沒有就是1
cout << sizeof(b);
return 0;
}
其他區(qū)別:
- 引用概念上定義一個變量的別名,指針存儲一個變量地址。
- 引用在定義時必須初始化,指針沒有要求
- 引用在初始化時引用一個實體后,就不能再引用其他實體,而指針可以在任何時候指向任何一個同類型實體
- 沒有NULL引用,但有NULL指針
- 在sizeof中含義不同:引用結(jié)果為引用類型的大小,但指針始終是地址空間所占字節(jié)個數(shù)(32位平臺下占4個字節(jié))
- 引用自加即引用的實體增加1,指針自加即指針向后偏移一個類型的大小
- 有多級指針,但是沒有多級引用
- 訪問實體方式不同,指針需要顯式解引用,引用編譯器自己處理
- 引用比指針使用起來相對更安全
2.內(nèi)聯(lián)函數(shù)
在學(xué)習(xí)c時,我們認識了宏
優(yōu)點: 1.增強代碼的復(fù)用性 2.提高性能。
缺點: 1.不方便調(diào)試宏(因為預(yù)編譯階段進行了替換) 2.導(dǎo)致代碼可讀性差,可維護性差,容易誤用。
3.沒有類型安全的檢查
為了解決缺點,c++中采用:
常量定義 換用const enum
短小函數(shù)定義 換用內(nèi)聯(lián)函數(shù)
2.1內(nèi)聯(lián)函數(shù)概念
以inline修飾的函數(shù)叫做內(nèi)聯(lián)函數(shù),編譯時C++編譯器會在調(diào)用內(nèi)聯(lián)函數(shù)的地方展開,沒有函數(shù)調(diào)用建立棧幀的開銷,內(nèi)聯(lián)函數(shù)提升程序運行的效率(用展開函數(shù)體來替代函數(shù)調(diào)用)
我們使用內(nèi)聯(lián)函數(shù):
2.2內(nèi)聯(lián)函數(shù)特性
- inline是一種以空間換時間的做法,如果編譯器將函數(shù)當(dāng)成內(nèi)聯(lián)函數(shù)處理,在編譯階段,會用函數(shù)體替換函數(shù)調(diào)用。 缺陷:可能會使目標(biāo)文件變大。 優(yōu)勢:少了調(diào)用開銷,提高程序運行效率。
- inline對于編譯器而言只是一個建議(會不會真的使用,看編譯器自己決定)不同編譯器關(guān)于inline實現(xiàn)機制可能不同,一般情況:將函數(shù)規(guī)模較小(即函數(shù)不是很長,具體沒有準(zhǔn)確的說法,取決于編譯器內(nèi)部實現(xiàn))、不是遞歸、且頻繁調(diào)用的函數(shù)采用inline修飾,否則編譯器會忽略inline特性
- inline不建議聲明和定義分離,分離會導(dǎo)致鏈接錯誤。因為inline被展開,就沒有函數(shù)地址了,鏈接就會找不到
關(guān)于第三點:內(nèi)聯(lián)函數(shù)因為直接展開,也就不要地址查詢(內(nèi)聯(lián)函數(shù)名不會進入符號表),我們之前經(jīng)常在頭文件里進行聲明,一個源文件里面進行實現(xiàn)?,F(xiàn)在在其他源文件里使用內(nèi)聯(lián)函數(shù)時不行的
3. auto關(guān)鍵字
隨著程序越來越復(fù)雜,程序中用到的類型也越來越復(fù)雜,經(jīng)常體現(xiàn)在:
- 類型過長難于拼寫
- 含義不明確導(dǎo)致容易出錯
auto就是來解決這個問題
3.1概念
C++11中,標(biāo)準(zhǔn)委員會賦予了auto全新的含義即:auto不再是一個存儲類型指示符,而是作為一個新的類型指示符來指示編譯器,auto聲明的變量必須由編譯器在編譯時期推導(dǎo)而得
int main()
{
auto a = 1;
auto b = 1.1;
auto c = 'c';//但這些都不是主要的使用場景
cout << typeid(a).name() << endl;
cout << typeid(b).name() << endl;
cout << typeid(c).name() << endl;
return 0;
}
3.2auto的使用細則
- auto與指針和引用結(jié)合起來使用
用
auto
聲明指針類型時,用auto
和auto*
沒有任何區(qū)別,但用auto
聲明引用類型時則必須加&
- 在同一行定義多個變量
當(dāng)在同一行聲明多個變量時,這些變量必須是相同的類型,否則編譯器將會報錯,因為編譯器實際只對第一個類型進行推導(dǎo),然后用推導(dǎo)出來的類型定義其他變量
3.3auto不能使用的場景
- auto不能作為函數(shù)的參數(shù)
- auto不能直接用來聲明數(shù)組
- 為了避免與C++98中的auto發(fā)生混淆,C++11只保留了auto作為類型指示符的用法
- auto在實際中最常見的優(yōu)勢用法就是跟以后會講到的C++11提供的新式for循環(huán),還有l(wèi)ambda表達式等進行配合使用
4.基于范圍的for循環(huán)(C++11)
4.1范圍for的語法
之前我們寫c的時候,在C++98中如果要遍歷一個數(shù)組,可以按照以下方式進行:
void Test1()
{
int arr[] = { 1, 2, 3, 4, 5 };
for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
arr[i] *= 2;
}
for (int* p = arr; p < arr + sizeof(arr) / sizeof(arr[0]); p++)
{
cout << *p << endl;
}
}
現(xiàn)在我們可以這樣:
void Test2()
{
int array[] = { 1, 2, 3, 4, 5 };
for (auto& e : array)//用for(int& e:array)也可以
{
e *= 2;
}
for (auto e : array)
{
cout << e << " ";
}
}
C++11中引入了基于范圍的for循環(huán)。for循環(huán)后的括號由冒號“ :”分為兩部分:第一部分是范圍內(nèi)用于迭代的變量,第二部分則表示被迭代的范圍
基于范圍的for循環(huán)會依次將容器中的元素賦值給迭代變量(通常命名為element)。在每次循環(huán)迭代中,迭代變量將會被賦值為容器中的下一個元素,直到遍歷完整個容器
如果想要改變數(shù)組里,就使用引用
void Test2()
{
int array[] = { 1, 2, 3, 4, 5 };
for (auto& e : array)
{
e *= 2;
cout << e << " ";
}
cout << endl;
for (auto e : array)
{
cout << e << " ";
}
}
4.2范圍for的使用條件
- for循環(huán)迭代的范圍必須是確定的
對于數(shù)組而言,就是數(shù)組中第一個元素和最后一個元素的范圍;對于類而言,應(yīng)該提供begin和end的方法,begin和end就是for循環(huán)迭代的范圍
- 迭代的對象要實現(xiàn)++和==的操作
5.指針空值nullptr(C++11)
我們經(jīng)常使用的NULL實際上是一個宏,在傳統(tǒng)的C頭文件(stddef.h)中,可以看到如下代碼:
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
可以看到,==NULL可能被定義為字面常量0,或者被定義為無類型指針(void)的常量==。不論采取何種定義,在使用空值的指針時,都不可避免的會遇到一些麻煩*
所以我們使用nullptr來對指針進行初始化,來替代NULL,以免NULL定義為0時出現(xiàn)錯誤
注意:文章來源:http://www.zghlxwxcb.cn/news/detail-799093.html
- 在使用nullptr表示指針空值時,不需要包含頭文件,因為nullptr是C++11作為新關(guān)鍵字引入的。
- 在C++11中,sizeof(nullptr) 與 sizeof((void*)0)所占的字節(jié)數(shù)相同。
- 為了提高代碼的健壯性,在后續(xù)表示指針空值時建議最好使用nullptr
好啦,c++入門的知識先到這里啦,下面就要開啟面向?qū)ο蟮钠铝恕8兄x大家支持?。?!文章來源地址http://www.zghlxwxcb.cn/news/detail-799093.html
到了這里,關(guān)于開啟C++之旅(下):引用、內(nèi)聯(lián)函數(shù)及現(xiàn)代特性(auto和范圍for循環(huán))的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!