網站首頁 編程語言 正文
一:引用
1.1:概念
引用不是定義一個新的變量,而是給已經存在的變量取一個別名。注意:編譯器不會給引用變量開辟內存空間,他和他的引用變量共用同一塊內存空間。
類型& 引用變量名(對象名) = 引用實體。
1.2:引用特性
1. 引用在定義時必須初始化
2. 一個變量可以有多個引用
3. 引用一旦引用一個實體,也就不能引用其他實體
通俗的講就是:我們取外號,肯定是對一個對象取外號,不可能是這空氣取外號,而且也可以給同一個人取多個外號,但是同一個外號我們就不要給多個人取了,那樣就亂套了,就不知道叫的是誰了。
1.3:常引用
原則:對原引用變量,權限只能縮小,不能放大。
int main(){
const int x = 20;
//int& y = x; // 放大
const int& y = x; // 不變
int c = 30;
const int& d = c; // 縮小
//cout << d << endl;
system("pause");
return 0;
}
對常變量取別名時,要加const;
const int & b = 10;
注意:當引用類型和引用實體不是同一類型時,如:
double a = 3.14;
//int &ra = a; //該編譯語句會報錯,因為類型不同
const int & ra = a;
//加上const就可以了。
我們可以這么理解,當浮點型轉換整型數據時,其中我們在C語言學過會發生隱式類型轉換,在轉換的時候會產生一個臨時變量,這個臨時變量具有常性,不可以被修改,如果不加const,那么權限被放大,所以需要加上const保證他的權限不變。
其實現在ra的地址已經不再是原變量a的地址了,是其中臨時變量的地址。
1.4:使用場景
做參數:
void Swap(int& x, int& y)
{
int tmp = x;
x = y;
y = tmp;
}
void Swap(double& x, double& y)
{
double tmp = x;
x = y;
y = tmp;
}
做返回值 下面我們先看兩個代碼
int & Add(int a, int b){
static int c = a + b;
return c;
}
int main(){
int & ret = Add(1, 2);
Add(3, 4);
cout << "Add(1, 2) is :" << ret << endl;
system("pause");
return 0;
}
Add(1, 2) is :3
//請按任意鍵繼續. . .?
int & Add(int a, int b){
int c = a + b;
return c;
}
int main(){
int & ret = Add(1, 2);
Add(3, 4);
cout << "Add(1, 2) is :" << ret << endl;
system("pause");
return 0;
}
Add(1, 2) is :7
//請按任意鍵繼續. . .
都一個結果是3,第二個結果是7,為什么會這樣呢?
我們知道在函數返回值時實際是產生一個臨時變量,若傳值返回,那么實際是發生了拷貝,若引用返回,那么其實是給這個臨時變量取了別名,返回了這個臨時變量的別名。 對于靜態變量c的作用域不變,但是生命周期變長,在Add函數返回時,出了作用域,返還對象并沒有還給系統,所以此時ret一直是第一次調用Add函數時產生的臨時變量的別名,所以ret的結果是3。
1.5:引用和指針的區別
在語法概念上,引用就是一個別名,沒有開辟獨立的空間存儲,和其引用實體共用同一塊實體。
int main(){
int a = 10;
int & ra = a;
cout << "&a = " << &a << endl;
cout << "&ra = " << &ra << endl;
system("pause");
return 0;
}
&a = 0137F8D8
&ra = 0137F8D8
請按任意鍵繼續. . .
由代碼結果可看是同一塊地址。
在底層實現上實際是有空間的,因為引用是按照指針方式來實現的。
引用和指針的不同點:
- 引用在定義時必須初始化,指針沒有要求。
- 引用在初始化時引用一個實體后,就不能再引用其他實體,而指針可以在任何時候指向任何一個同類型實體。
- 沒有NULL引用,但是有NULL指針。
- sizeof中含義不同:引用結果為引用類型的大小,但指針始終是地址空間所占字節個數。
- 引用自加即引用的實體增加1,指針自加即指針向后偏移一個類型的大小。
- 有多級指針,但沒有多級引用。
- 訪問實體不同,指針需要顯式解引用,引用編譯器自己處理。
- 引用比指針使用起來更加安全。
二:內聯函數
2.1:概念
以inline修飾的函數叫做內聯函數,用于解決C語言中宏函數難懂易錯的缺陷。在編譯時C++編譯器會在調用內聯函數的地方展開,沒有函數壓棧的開銷,內聯函數提升程序運行的效率。
如果在上述函數前增加inline關鍵字,將其改成內聯函數,那么在編譯期間編譯器會用函數體(展開)替換函數的調用。
查看方式:
在Release模式下,查看編譯器生成的匯編代碼中是否有call Add
在Debug模式下,需要對編譯器進行設置,否則也不會展開。
如VS2013版本設置:
右擊項目名稱——>屬性:
2.2:特性
- inline是一種以空間換取時間的做法,利用直接展開函數省去調用函數的開銷。所以對于代碼很長或者有循環/遞歸的函數不方便使用作為內聯函數。
- inline對于編譯器而言只是一個建議,編譯器會自動優化,如果定義的inline的函數體內有循環/遞歸等,編譯器優化時會忽略掉內聯。
- inline不建議聲明和定義分開,分開會導致鏈接錯誤,因為inline被展開,就沒有函數地址了,如果分開了,鏈接的時候就找不到了。
2.3:面試題
宏的優點?
- 增強代碼的復用性
- 提高性能
缺點:
- 不方便調試,因為編譯階段進行了宏替換。
- 導致代碼可讀性差,可維護性差,容易誤用。
- 沒有類型安全檢查。
C++哪些技術可以替代宏?
- 常量定義,換用const。
- 函數定義,換用內聯函數。
三:auto關鍵字
3.1:auto簡介
我們在學習C語言的時候就見過auto,當時認為使用auto修飾的變量是具有自動存儲器的局部變量,但我們幾乎沒有使用。
在C++11中,標準委員會賦予了auto全新的含義:auto不再是一個存儲類型標識符,而是作為一個新的類型指示符來指示編譯器,auto聲明的變量必須由編譯器在編譯時期推導而得。
int TestAuto()
{
return 10;
}
int main()
{
const int a = 10;
auto b = &a;
auto c = 'a';
auto d = TestAuto();
cout << typeid(b).name() << endl;
cout << typeid(c).name() << endl;
cout << typeid(d).name() << endl;
return 0;
}
這里我們看打印的結果是:
int const *
char
int
請按任意鍵繼續. . .
可見auto關鍵字可以自動識別變量的類型。這里**typeid(b).name()**可返回變量類型的字符串。
【注意】:使用auto關鍵字定義變量時,必須對其初始化,在編譯階段編譯器需要根據初始化表達式來推導auto的實際類型。因此auto并非是一種類型的聲明,而是一種類型聲明時的占位符,編譯器在編譯期間會將auto替換為變量實際類型。
3.2:auto使用細則
auto與指針和引用結合使用
用auto聲明指針類型時,用auto和auto*沒有任何區別,但是用auto聲明引用類型時則必須加&。
int main()
{
int x = 10, y = 20;
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;
system("pause");
return 0;
}
int *
int *
int
請按任意鍵繼續. . .
看這里a和b的類型都是int * ,所以用auto聲明指針類型時,用auto和auto*沒有任何區別。
在同一行定義多個變量當在同一行聲明多個變量的時候,這些變量必須是同一類型的,否則編譯器會出錯,因為編譯器實際只對第一個類型進行推導,然后用推導出來的類型定義其他變量。
注意看d下面編譯器報錯了,因為c和d的初始化表達式類型不同。
3.3:auto不能推導的場景
1:auto不可以作為函數形參
因為在形參處使用auto,編譯器無法對a的實際類型進行推導。
2:auto不可以用來聲明數組。
四:基于范圍的for循環
4.1:范圍for循環的語法
對于一個有范圍的集合而言,在C++98中由程序員來說明循環的范圍是多余的,有時候還容易犯錯。因此在C++11中引入基于范圍的for循環。for循環后的括號由冒號“:”分為兩部分。第一部分是范圍內用于迭代的變量,第二部分則是表示被迭代的范圍。
int main(){
int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9};
for (int i = 0; i < sizeof(array) / sizeof(int); ++i){
array[i] *= 2;
}
for (int i = 0; i < sizeof(array) / sizeof(int); ++i){
cout << array[i] << " ";
}
cout << endl;
// 范圍for
// 依次自動取array中的數據,賦值給e,自動判斷結束
for (auto& e : array){
e /= 2;
}
for (auto x : array){
cout << x << " ";
}
cout << endl;
system("pause");
return 0;
}
2 4 6 8 10 12 14 16 18
1 2 3 4 5 6 7 8 9
請按任意鍵繼續. . .
對于語句auto x : array意思是依次自動取array中的數據,賦值給e,所以相當于e是array中的拷貝,既然是拷貝,也就不能對array中的數據產生影響。對要改變array中數據,需要利用引用,正如代碼中auto& e : array。
4.2:范圍for循環的使用條件
for循環迭代的范圍必須是確定的。
下面給出一個錯誤代碼示例:
void TestFor(int array[])
{
for (auto& e : array)
cout << e << endl;
}
因為我們知道,函數傳參,形參相對于實參發生降維,形參array是一個整型指針,所以這里for循環的迭代范圍是不確定的。
原文鏈接:https://blog.csdn.net/qq_43727529/article/details/124709088
相關推薦
- 2022-03-11 .NET6中使用CuteEditor詳解_實用技巧
- 2022-12-23 Kotlin?select使用方法介紹_Android
- 2022-09-24 VisualStudio?制作Dynamic?Link?Library動態鏈接庫文件的詳細過程_C
- 2022-09-18 ASP.NET實現文件上傳_實用技巧
- 2022-09-24 React報錯之Object?is?possibly?null的問題及解決方法_React
- 2022-07-12 strcpy、strncpy與memcpy的區別你了解嗎?
- 2022-04-16 C#算法之實現阿姆斯特朗數_C#教程
- 2022-06-28 .Net連接數據庫的方式詳解_實用技巧
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支