【C++從練氣到飛升】01---C++入門

?🎈個人主頁:庫庫的里昂
?收錄專欄:C++從練氣到飛升
🎉鳥欲高飛先振翅,人求上進先讀書。

目錄

推薦

前言

什么是C++

C++的發展史

📋命名空間

命名空間定義

命名空間使用

命名空間的嵌套

std命名空間的使用

📋C++輸入&輸出

缺省參數的定義

缺省參數分類

缺省參數出現的位置

📋函數重載

函數重載的概念

函數重載的種類

C++支持函數重載的原理

📋引用

引用的概念

引用的特性

引用的使用場景

傳值和引用性能比較

常引用

引用和指針的區別?

📋內聯函數

內聯函數的概念

內聯函數的特征

📋auto關鍵字

auto簡介

auto使用規則

auto無法使用的場景

📋基于范圍的for循環

范圍for的語法

范圍for的使用條件

📋指針空值nullptr


??推薦

前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到網站

前言

什么是C++

????????C語言是結構化和模塊化的語言,適合處理較小規模的程序。對于復雜的問題,規模較大的程序,需要高度的抽象和建模時,C語言則不合適。為了解決軟件危機, 20世紀80年代, 計算機界提出了OOP(object oriented programming:面向對象)思想,支持面向對象的程序設計語言應運而生。
????????1982年,Bjarne Stroustrup博士在C語言的基礎上引入并擴充了面向對象的概念,發明了一種新的程序語言。為了表達該語言與C語言的淵源關系,命名為C++。因此:C++是基于C語言而產生的,它既可以進行C語言的過程化程序設計,又可以進行以抽象數據類型為特點的基于對象的程序設計,還可以進行面向對象的程序設計。

C++的發展史

????????1979年,貝爾實驗室的本賈尼等人試圖分析unix內核的時候,試圖將內核模塊化,于是在C語言的基礎上進行擴展,增加了類的機制,完成了一個可以運行的預處理程序,稱之為C with classes。

語言的發展就像是練功打怪升級一樣,也是逐步遞進,由淺入深的過程。我們先來看下C++的歷史版本。

C++還在不斷的向后發展。但是:現在公司主流使用還是C++98和C++11,所有大家不用追求最新,重點將C++98和C++11掌握好,等工作后,隨著對C++理解不斷加深,有興趣的小伙伴可以去琢磨下更新的特性。

📋命名空間

在C/C++中,變量、函數和后面要學到的類都是大量存在的,這些變量、函數和類的名稱將都存在于全局作用域中,可能會導致很多沖突。使用命名空間的目的是對標識符的名稱進行本地化,以避免命名沖突或名字污染,namespace關鍵字的出現就是針對這種問題的。

#include <stdio.h>
#include <stdlib.h>int rand = 10;// C語言沒辦法解決類似這樣的命名沖突問題,所以C++提出了namespace來解決
int main()
{printf("%d\n", rand);return 0;
}// 編譯后后報錯:error C2365: “rand”: 重定義;以前的定義是“函數”
命名空間定義

命名空間的定義由兩部分構成:首先是關鍵字namespace,后面跟命名空間的名字,然后接一對{},{}中即為命名空間的成員。?命名空間中可以定義變量、函數、類型和其他命名空間。

namespace N1//命名空間的名字
{//定義變量int rand = 10;//定義函數int Add(int left, int right){return left + right;}//定義類型struct Node{struct Node* next;int val;};//嵌套命名空間namespace N2{int Sub(int left, int right){return left - right;}}
}

注意:

  • 一個命名空間就定義了一個新的作用域,命名空間中的所有內容都局限于該命名空間中。
  • 用一個工程中允許出現多個相同名稱的命名空間,編譯器最后會將它們合并為一個命名空間
命名空間使用

命名空間的使用有三種方式:

  • 加命名空間名稱及域作用限定符
namespace N
{int a=10;int b=5;
}int main()
{printf("%d\n", N::a);return 0;    
}
  • 使用 using 將命名空間中某個成員引入
using N::b;
int main()
{printf("%d\n", N::a);printf("%d\n", b);return 0;    
}
  • 使用 using namespace 命名空間名稱引入(展開命名空間)
namespace N
{int a=10;int b=5;
}int a=20;using namespce N;int main()
{printf("%d\n", a);      //a不明確,有二義性printf("%d\n", ::a);    //訪問全局的a  printf("%d\n", N::a);   //訪問N中的aprintf("%d\n", b);return 0;    
}

N中的成員a?就與全局作用域中的a?產生了沖突。這種沖突是允許存在的,但是要想使用沖突的名字,我們就必須明確指出名字的版本。main函數中所有未加限定的a都會產生二義性錯誤。

這時我們必須使用域作用限定符(::)來明確指出所需的版本

  • :?:a來表示全局作用域中的a
  • N: :a來表示定義在N中的a

注意:

如果命名空間沒有展開,編譯器默認是不會搜索命名空間中的變量,去訪問變量是訪問不到的。

訪問的優先級:局部域 > 全局域

命名空間的嵌套

嵌套的命名空間同時是一個嵌套的作用域,它嵌套在外層命名空間的作用域中。嵌套的命名空間中的名字遵循的規則與往常類似:內層命名空間聲明的名字將隱藏外層命名空間聲明的同名成員。在嵌套的命名空間中定義的名字只在內層命名空間中有效,外層命名空間的代碼想要訪問它必須在名字前添加限定符。

namespace N
{int a = 10;namespace N1{int a = 20;    //將外層作用域的a隱藏了int b = 15;namespace N2{int c = N1::b;}
}int main()
{printf("%d\n", N::N2::c);printf("%d\n", N::N1::a);printf("%d\n", N::a);return 0;
}
std命名空間的使用

std是C++標準庫的命名空間,如何展開std使用更合理呢?

  1. 在日常練習中,建議直接using namespace std;即可,這樣就很方便。
  2. ?using namespace std;展開,標準庫就全部暴露出來了,如果我們定義跟庫重名的類型、對象、函數,就存在沖突問題。該問題在日常練習中很少出現,但是項目開發中代碼較多、規模 大,就很容易出現。所以建議在項目開發中使用,像std::cout這樣使用時指定命名空間例如: using std::cout展開常用的庫對象、類型等方式。?

📋C++輸入&輸出

#include <iostream>
using namespace std;int main()
{int a = 10;double b = 10.5;cout << a << endl;cout << b << endl;return 0;
}

我們在項目中要經常使用?cout?和?endl,每次指定命名空間很不方便,直接展開會全部暴露,有沖突風險,我們可以指定展開來解決問題。

using std::cout;
using std::endl;

說明:

  • 使用cout標準輸出對象(控制臺)cin標準輸入對象(鍵盤)時,必須包含< iostream >頭文件以及按命名空間使用方法使用std。
  • cout?和?cin?是全局的流對象,endl?是特殊的C++符號,表示換行輸出,他們都包含在包含?< iostream >頭文件中。
  • <<是流插入運算符,>>是流提取運算符。
  • 使用C++輸入輸出更方便,不需要像 printf和scanf 輸入輸出時那樣,需要手動控制格式。?C++的輸入輸出可以自動識別變量類型。

📋缺省參數

缺省參數的定義

? ? ?缺省參數是聲明或定義函數時為函數的參數指定一個缺省值。在調用該函數時,如果沒有指定實參則采用該形參的缺省值,否則使用指定的實參。

void Func(int a = 5)
{cout << a << endl;
}int main()
{Func();     // 沒有傳參時,使用參數的默認值Func(10);   // 傳參時,使用指定的實參return 0;
}

上面代碼在第一次調用?Func()?時,沒有傳遞參數,a?就使用了缺省值。

缺省參數分類
  • 全缺省參數 -- 所有參數都給了缺省值
void Func(int a = 10, int b = 20, int c = 30)
{cout<<"a = "<<a<<endl;cout<<"b = "<<b<<endl;cout<<"c = "<<c<<endl;
}int main()
{Func(1,2,3);    Func(1,2); Func(1); Func();  return 0;
}

全缺省參數在傳參時,參數是按照從左往右的順序進行缺省的,不能跳著缺省,例如:Func(1,? ,3) ,讓第一個形參和第三個形參都使用傳遞值,而讓第二個參數使用缺省值,這種做法是不被允許的。

  • 半缺省參數 --?部分的參數給了缺省值
void Func(int a, int b = 20, int c = 30)
{cout<<"a = "<<a<<endl;cout<<"b = "<<b<<endl;cout<<"c = "<<c<<endl;
}int main()
{Func(1,2,3);    Func(1,2); Func(1);  return 0;
}

半缺省參數必須從右往左依次來給出,不能間隔著給。

注意:

  1. 缺省參數不能在函數聲明和定義中同時出現,只能出現在函數聲明中。
  2. 缺省值必須是常量或者全局變量。
缺省參數出現的位置

??? ??缺省參數只能出現在函數聲明中,如下面的代碼,在聲明和定義中都給了缺省參數,而且給定的值不相同,就不知道以哪個值為準。

//a.h
void Func(int a = 10);//a.cpp
void Func(int a = 20)
{}

不能只在聲明處給缺省參數,如下面的代碼,如果只在聲明處給缺省參數,在其他的文件中沒有缺省參數,就不知是什么值。

//a.cpp
void Func(int a = 10)
{}//b.cpp
void Func(int a)
{}

📋函數重載

? ? ?自然語言中,一個詞可以有多重含義,人們可以通過上下文來判斷該詞真實的含義,即該詞被重載了。 比如:以前有一個笑話,國有兩個體育項目大家根本不用看,也不用擔心。一個是乒乓球,一個 是男足。前者是“誰也贏不了!”,后者是“誰也贏不了!”

函數重載的概念

? ? ?函數重載是函數的一種特殊情況,C++允許在同一作用域中聲明幾個功能類似的同名函數,這些同名函數的形參列表(參數個數或類型或類型順序)不同,常用來處理實現功能類似數據類型不同的問題。

函數重載的種類
  • 參數類型不同
int Add(int left, int right)
{cout << "int Add(int left, int right)" << endl;return left + right;
}double Add(double left, double right)
{cout << "double Add(double left, double right)" << endl;return left + right;
}int main()
{cout << Add(1, 2) << endl;cout << Add(1.0, 2.0) << endl;
}

上面的代碼定義了兩個同名的Add函數,但是它們的參數類型不同,第一個函數的兩個參數都是int型,第二個函數的兩個參數都是double型,在調用Add函數的時候,編譯器會根據所傳實參的類型自動判斷調用哪個函數。

  • 參數個數不同
void Fun()
{cout << "f()" << endl;
}void Fun(int a)
{cout << "f(int a)" << endl;
}int main()
{Fun();Fun(1);return 0;
}

  • 參數類型順序不同
void Text(int a, char b)
{cout << "Text(int a,char b)" << endl;
}void Text(char b, int a)
{cout << "Text(char b, int a)" << endl;
}int main()
{Text(1, 'a');Text('a', 1);return 0;
}
  • 有缺省參數的情況
void Fun()
{cout << "f()" << endl;
}void Fun(int a = 10)
{cout << "f(int a)" << endl;
}int main()
{Fun();     //無參調用會出現歧義Fun(1);    //調用的是第二個return 0;
}

上面代碼中的兩個Fun函數構成函數重載,編譯可以通過,因為第一個沒有參數,第二個有一個整型參數,屬于上面的參數個數不同的情況。但是Fun函數存在一個問題:在沒有參數調用的時候會產生歧義,因為有缺省參數,所以對兩個Fun函數來說,都可以不傳參。

注意:返回值的類型與函數是否構成重載無關。

C++支持函數重載的原理

? ? ?在C/C++中,一個程序要運行起來,需要經歷以下幾個階段:預處理、編譯、匯編、鏈接。

我們想理解清楚函數重載,還要了解函數簽名的概念,函數簽名包含了一個函數的信息,包括函數名、它的參數類型、他所在的類和名稱空間以及其他信息。函數簽名用于識別不同的函數。 C++編譯器和鏈接器都使用符號來標識和處理函數和變量,所以對于不同函數簽名的函數,即使函數名相同,編譯器和鏈接器都認為他們是不同的函數。

Linux環境下采用C語言編譯器編譯后結果

可以看出經過gcc編譯后,函數名字的修飾沒有發生改變。這也就是為什么C語言不支持函數重
載,因為同名函數沒辦法區分。

采用C++編譯器編譯后結果

其中_Z是固定的前綴;3表示函數名的長度;Add就是函數名;i是int的縮寫,兩個i表示兩個參數都是int類型,d是double的縮寫,兩個d表示兩個參數都是double類型。C++就是通過函數修飾規則來區分,只要參數不同,修飾出來的名字就不一樣,就支持了重載。通過分析可以發現,修飾后的名稱中并不包含任何于函數返回值有關的信息,因此也驗證了上面說的返回值的類型與函數是否構成重載無關。

總結:

  1. C語言之所以沒辦法支持重載,是因為同名函數沒辦法區分。而C++是通過函數修飾規則來區分,只要參數不同,修飾出來的名字就不一樣,就支持了重載。
  2. 如果兩個函數函數名和參數是一樣的,返回值不同是不構成重載的,因為調用時編譯器沒辦法區分。

📋引用

引用的概念

引用不是新定義一個變量,而是給已存在變量取了一個別名,編譯器不會為引用變量開辟內存空 間,它和它引用的變量共用同一塊內存空間

類型& 引用變量名(對象名) = 引用實體

int main()
{int a = 0;int& b = a;//定義引用類型,b是a的引用return 0;
}

注意:引用類型必須和引用實體是同種類型的

引用的特性
  • 引用在定義時必須初始化
int main()
{int a = 10;int& b;    //錯誤的return 0;
}

在使用引用時,我們必須對變量進行初始化。int& b = a;,這樣的代碼才是被允許的。

  • 引用不能改變指向
int main()
{int a = 10;int b = 20;int& c = a;c = b;return 0;
}

我們可以看到b和c的地址不同,所以c = b表示的不是c是b引用,而是是把b變量的值賦值給c引用的實體,c依舊是a的引用,所以引用一旦引用一個實體,再不能引用其他實體,也就是引用不能改變指向。

引用的使用場景

🎀做參數

引用做參數的意義:

  • 做輸出型參數,即要求形參的改變可以影響實參
  • 提高效率,自定義類型傳參,用引用可以避免拷貝構造,尤其是大對象和深拷貝對象

交換兩個整型變量:?

void Swap(int& num1, int& num2)
{int tmp = num1;num1 = num2;num2 = tmp;
}int main()
{int a = 5;int b = 10;Swap(a,b);return 0;
}

如上代碼,我們可以使用引用做參數實現了兩個數的交換,num1是?a?的引用,和?a?在同一塊空間,對num1的修改也就是對?a?修改,?b?同理,所以在函數體內交換num1和num2實際上就是交換?a?和?b?。以前交換兩個數的值,我們需要傳遞地址,還要進行解引用,相對繁瑣。

交換兩個指針變量:

void Swap(int*& p1, int*& p2)
{int* tmp = p1;p1 = p2;p2 = tmp;
}int main()
{int a = 5;int b = 10;int* pa = &a;int* pb = &b;Swap(pa,pb);return 0;
}

如果用C語言來實現交換兩個指針變量,實參需要傳遞指針變量的地址,那形參就需要用二級指針來接收,這顯然十分容易出錯。有了引用之后,實參直接傳遞指針變量即可,形參用指針類型的引用。

🎀做返回值

引用做返回值的意義:

  • 減少拷貝,提高效率。
  • 可以同時讀取和修改返回對象
int& add(int x, int y)
{int sum = x + y;return sum;
}int main()
{int a = 5;int b = 10;int ret = add(a, b);cout << ret << endl;return 0;
}

如上代碼,我們使用傳值返回,調用函數要創建棧幀,sum是add函數中的一個局部變量,存儲在當前函數的棧幀中,函數調用結束棧幀銷毀,sum也會隨之銷毀,對于這種傳值返回,會生成一個臨時的中間變量,用來存儲返回值,在返回值比較小的情況下,這個臨時的中間變量一般就是寄存器。

如上代碼,傳引用就是給sum起了一個別名,返回的值就是sum的別名,但是這里會出現問題,函數調用結束棧幀銷毀,sum也會隨之銷毀,返回它的值再進行調用就是越界訪問,打印出的值為隨機值。

可是這里的值為什么是正確的呢?這是取決于編譯器的,看編譯器是否會對這塊空間進行清理。

傳值和引用性能比較

? ? ?以值作為參數或者返回值類型,在傳參和返回期間,函數不會直接傳遞實參或者將變量本身直 接返回,而是傳遞實參或者返回變量的一份臨時的拷貝,因此用值作為參數或者返回值類型,效 率是非常低下的,尤其是當參數或者返回值類型非常大時,效率就更低。

struct A
{int a[100000];
};void TestFunc1(A a)
{;
}void TestFunc2(A& a)
{;
}void TestFunc3(A* a)
{;
}//引用傳參————可以提高效率(大對象或者深拷貝的類對象)
void TestRefAndValue()
{A a;// 以值作為函數參數size_t begin1 = clock();for (size_t i = 0; i < 10000; ++i)//就是單純的調用一萬次這個函數傳一萬次參TestFunc1(a);size_t end1 = clock();// 以引用作為函數參數size_t begin2 = clock();for (size_t i = 0; i < 10000; ++i)TestFunc2(a);//這里直接傳的是變量名size_t end2 = clock();//以指針作為函數參數size_t begin3 = clock();for (int i = 0; i < 10000; i++){TestFunc3(&a);}size_t end3 = clock();// 分別計算兩個函數運行結束后的時間cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;cout << "TestFunc3(A*)-time:" << end3 - begin3 << endl;
}

值和引用的作為返回值類型的性能比較:

struct A
{int a[100000];
};
A a;//全局的,函數棧幀銷毀后還在// 值返回
A TestFunc1()
{return a;
}// 引用返回
A& TestFunc2()
{return a;
}
void TestReturnByRefOrValue()
{// 以值作為函數的返回值類型size_t begin1 = clock();for (size_t i = 0; i < 100000; ++i)TestFunc1();//就讓他返回不接收size_t end1 = clock();// 以引用作為函數的返回值類型size_t begin2 = clock();for (size_t i = 0; i < 100000; ++i)TestFunc2();size_t end2 = clock();// 計算兩個函數運算完成之后的時間cout << "TestFunc1 time:" << end1 - begin1 << endl;cout << "TestFunc2 time:" << end2 - begin2 << endl;
}int main()
{TestReturnByRefOrValue();return 0;
}

常引用

🎀?權限放大

int main()
{const int a = 10;int& b = a;return 0;
}

???????上面代碼中,用const定義了一個常變量?a?,然后給a取一個別名?b?,這段代碼在編譯過程中出現了錯誤,這是為什么呢?

? ? ??a?是一個常變量,是不可以被修改的,給?a?取別名為變量?b?,變量b沒有用const修飾,所以不具有常屬性,是可以被修改的,相當于權限的放大,這種情況是不允許的。正確的做法是:

int main()
{const int a = 10;const int& b = a;return 0;
}

🎀?權限縮小

int main()
{int a = 10;const int& b = a;return 0;
}

????????上面代碼中,給一個普通的變量a取了一個別名b,這個b是一個常引用。這意味著,可以通過a變量去對內存中存儲的數據進行修改,但是不能通過b去修改內存中存儲的數據,但是b會跟著變。

引用和指針的區別?
  • 引用在概念上定義一個變量的別名,指針存儲一個變量的地址
  • 引用在定義時必須初始化,指針沒有要求。
  • 引用在初始化時引用一個一個實體后,就不能再引用其他實體,而指針可以在任何時候指向任何一個同類型實體。
  • 沒有NULL引用,但有NULL空指針。
  • 在sizeof中的含義不同,引用結果為引用類型的大小,但指針始終是地址空間所占字節個數(32位機下占四個字節,64位機下占八個字節)。
  • 引用自加即引用的實體增加1,指針自加即指針向后偏移一個類型的大小。
  • 有多級指針,但是沒多級引用
  • 訪問實體方式不同。指針顯式解引用,引用編譯器自己做處理。
  • 引用比指針使用起來相對更安全。

📋內聯函數

? ???普通的函數在調用的時候會開辟函數棧幀,會產生一定量的消耗,在C語言中可以用宏函數來解決這個問題,但是宏存在以下缺陷:復雜、容易出錯、可讀性差、不能調試。為此,C++中引入了內聯函數這種方法。.

內聯函數的概念

? ? ?以inline修飾的函數叫做內聯函數,編譯時C++編譯器會在調用內聯函數的地方展開,沒有函數調 用建立棧幀的開銷,內聯函數提升程序運行的效率。

int Add(int x, int y)
{return x + y ;
}int main()
{int ret = 0;ret = Add(3, 5);cout << ret << endl;return 0;
}

🎀內聯函數

inline int Add(int x, int y)
{return x + y ;
}

內聯函數在編譯期間編譯器會用函數體替換函數的調用。

注意:在默認的Debug模式下,內聯函數是不會展開的。

查看方式:

  • 在release模式下,查看編譯器生成的匯編代碼中是否存在call Add。
  • 在debug模式下,需要對編譯器進行設置,否則不會展開,需要進行設置,設置過程如下:

內聯函數的特征
  1. inline是一種以空間換時間的做法,如果編譯器將函數當成內聯函數處理,在編譯階段,會用函數體替換函數調用,缺陷:可能會使目標文件變大,優勢:少了調用開銷,提高程序運行效率。?
  2. inline對于編譯器而言只是一個建議,不同編譯器關于inline實現機制可能不同,一般建議:將函數規模較小(即函數不是很長,具體沒有準確的說法,取決于編譯器內部實現)、不是遞歸、且頻繁調用的函數采用inline修飾,否則編譯器會忽略inline特性。
  3. inline不建議聲明和定義分離,分離會導致鏈接錯誤。因為inline被展開,就沒有函數地址 了,鏈接就會找不到。

📋auto關鍵字

?????????隨著程序越來越復雜,程序中用到的類型也越來越復雜,例如:

#include <vector>
#include <string>int mian()
{vetcor<string> v;vetcor<string>::iterator it = v.begin();return 0;
}

?vetcor<string>::iterator是一個類型,但是該類型太長了,特別容易寫錯。在C語言中,我們可以通過?typedef?給類型取別名,比如:

typedef vetcor<string>::iterator Map;

使用 typedef 給類型取別名確實可以簡化代碼,但使用 typedef 又會遇到新的問題。在編程時,常常需要把表達式的值賦值給變量,這就要求在聲明變量的時候清楚地知道表達式的類型。但這點有時很難做到,因此C++11給auto賦予了新的含義。

auto it = v.degin();
auto簡介

在早期C/C++中auto的含義是:使用auto修飾的變量,是具有自動存儲器的局部變量, 但在C++11中:auto不再是一個存儲類型指示符,而是作為一個新的類型指示符來指示編譯器,auto聲明的變量必須由編譯器在編譯時期推導而得。簡單來說,auto會根據表達式自動推導類型

int main()
{int a = 0;auto b = a;auto& c = a;auto* d = &a;//typeid可用來查看變量類型cout << typeid(b).name() << endl;cout << typeid(c).name() << endl;cout << typeid(d).name() << endl;return 0;
}

注意:

? ? ?使用auto定義變量時必須對其進行初始化,在編譯階段編譯器需要根據初始化表達式來推導auto 的實際類型。因此auto并非是一種“類型”的聲明,而是一個類型聲明時的“占位符”,編譯器在編譯期會將auto替換為變量實際的類型。

int main()
{auto a;    //要初始化return 0;
}

auto使用規則

🎀auto與指針和引用結合起來使用

  • 用auto聲明指針類型時,用auto和auto*沒有任何區別
int main()
{int x = 10;auto a = &x;auto* b = &x;cout << typeid(a).name() << endl;cout << typeid(b).name() << endl;return 0;
}

  • 用auto聲明引用類型時,則必須加&
int main()
{int x = 10;auto& a = x;cout << typeid(a).name() << endl;return 0;
}

🎀在同一行定義多個變量?

? ???當在同一行聲明多個變量的時候,這些變量必須是相同的類型,否則編譯器會報錯,因為編譯器實際只對第一個類型進行推導,然后用推導出來的類型定義其他變量。

int main()
{auto a = 10, b = 30;auto c = 15, d = 1.5;//該行編譯失敗,c和d的初始化類型不同
}
auto無法使用的場景

🎀auto不能作為函數的參數

//錯誤,編譯器無法對x的實際類型進行推導
void Text(auto x)
{}int main()
{int a=5;Test(a);return 0;
}

🎀auto不能作返回值

auto Test(int x)
{}

🎀auto不能直接用來聲明數組

void Text()
{auto arr[] = { 1, 2, 3 };//錯誤寫法int arr[] = {1, 2, 3}//這才是正確寫法
}

📋基于范圍的for循環

范圍for的語法

? ? ?我們在以前使用 for 遍歷一個數組,會用下面這種方法:

int main()
{int arr[] = { 1,2,3,4,5 };int size = sizeof(arr) / sizeof(arr[0]);for (int i = 0; i < size; ++i){cout << arr[i] << " ";}cout << endl;
}

對于一個有范圍的集合而言,由程序員來說明循環的范圍是多余的,有時候還會容易犯錯誤。因此C++11中引入了基于范圍的for循環。for循環后的括號由冒號“ :”分為兩部分:第一部分是范 圍內用于迭代的變量,第二部分則表示被迭代的范圍。

int main()
{int arr[] = { 1,2,3,4,5 };int size = sizeof(arr) / sizeof(arr[0]);for (auto e : arr){cout << e << " ";}return 0;
}

依次取數組arr中的每個數賦值給e,e也就是數組中每個數的拷貝,所以e的改變不會影響數組中數的改變,想要改變數組的值,要使用引用。

int main()
{int arr[] = { 1,2,3,4,5 };int size = sizeof(arr) / sizeof(arr[0]);for (auto e : arr){e++;cout << e << " ";}cout << endl;for (auto e : arr){cout << e << " ";}return 0;
}

范圍for的使用條件
  1. for循環迭代的范圍必須是確定的
  2. 迭代的對象要實現++和==的操作

對于數組而言,就是數組中第一個元素和最后一個元素的范圍;對于類而言,應該提供 begin 和end的方法,begin和end就是for循環迭代的范圍。

注意:以下代碼就有問題,因為for的范圍不確定?

void Text(int arr[])//arr本質上只是一個地址,沒有范圍
{for (auto a : arr){cout << a << endl;}
}

數組不能傳參,數組傳參傳遞的是數組首元素的地址

📋指針空值nullptr

? ? ?在C/C++編程習慣中,我們聲明一個變量時最好給該變量一個合適的初始值,否則可能會出現 不可預料的錯誤,比如未初始化的指針。如果一個指針沒有合法的指向,我們都會把它置為空指針。

void Test()
{int* p1 = NULL;int* p2 = 0;
}

?NULL實際是一個宏,在傳統的C頭文件(stddef.h)中,可以看到如下代碼:

#ifndef NULL
#ifdef __cplusplus
#define NULL   0
#else
#define NULL   ((void *)0)
#endif
#endif

下面這段代碼的結果是什么呢?

void f(int)
{cout<<"f(int)"<<endl;
}void f(int*)
{cout<<"f(int*)"<<endl;
}int main()
{f(0);f(NULL);f(nullptr);return 0;
}

程序本意是想通過 f(NULL) 調用 f(int*) 函數,但是由于NULL被定義成0,因此與程序的初衷相悖。 在C++98中,字面常量0既可以是一個整形數字,也可以是無類型的指針(void*)常量,但是編譯器默認情況下將其看成是一個整形常量,如果要將其按照指針方式來使用,必須對其進行強轉(void *)0。?

注意:

  1. ?在使用nullptr表示指針空值時,不需要包含頭文件,因為nullptr是C++11作為新關鍵字引入的。
  2. ?在C++11中,sizeof(nullptr) 與 sizeof((void*)0)所占的字節數相同。
  3. 為了提高代碼的健壯性,在后續表示指針空值時建議最好使用nullptr。

本次的內容到這里就結束啦。希望大家閱讀完可以有所收獲,同時也感謝各位讀者三連支持。文章有問題可以在評論區留言,博主一定認真認真修改,以后寫出更好的文章。你們的支持就是博主最大的動力。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/718431.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/718431.shtml
英文地址,請注明出處:http://en.pswp.cn/news/718431.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

編譯 qsqlmysql.dll QMYSQL driver not loaded

Qt 連接MySQL數據庫&#xff0c;沒有匹配的qsqlmysql.dll, 需要我們跟進自己Mysql 以及QT版本自行編譯的。異常如下圖&#xff1a; 安裝環境為 VS2019 Qt5.12.12&#xff08;msvc2017_64、以及源碼&#xff09; 我的安裝地址&#xff1a;D:\Qt\Qt5.12.12 Mysql 8.1.0 默認安…

Java 定時器

Java 定時器 package com.su.test.threadtest; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; import java.util.TimerTask; /** 在這里插入代碼片使用定時器指定定時任務。timer&#xff1a;計時器…

2023年下半年教師資格證考試《教育知識與能力》(中學)題

3.李老師在初二選擇了人數、性別比例、學習成績、教材各方面情況相同的兩個班進行教學&#xff0c;對其中一班采用講授法&#xff0c;對另一個班采用自學輔導法&#xff0c;經過一個階段的教學后進行測驗&#xff0c;以比較兩種方法教學效果&#xff0c;李老師采用的方法屬于&a…

RMSNorm 類中引入一些參數

在 RMSNorm 類中&#xff0c;引入可學習的參數&#xff0c;以增強模型的表達能力和適應性。以下是一些常見的方法&#xff1a; 可學習的縮放參數&#xff08;Scale&#xff09;&#xff1a; 除了 self.weight&#xff0c;可以為每個維度引入一個可學習的縮放參數。這可以通過創…

PyTorch 中的 apply

Abstract nn.Module[List].apply(callable)Tensor.apply_(callable) → TensorFunction.apply(Tensor...) nn.Module[List].apply()? 源碼: def apply(self: T, fn: Callable[[Module], None]) -> T:"""Typical use includes initializing the paramete…

大語言模型LLM代碼:PyTorch庫與ChatGLM模型(LLM系列21)

文章目錄 通過閱讀大語言模型的代碼,熟悉并理解PyTorch大語言模型LLM代碼:PyTorch庫與ChatGLM模型(LLM系列21)大語言模型中的PyTorchChatGLM3-6B模型代碼ChatGLMModel類總覽ChatGLMModel類說明ChatGLMModel類核心代碼片段通過閱讀大語言模型的代碼,熟悉并理解PyTorch 大語…

基于yolov5的飛機蒙皮缺陷檢測系統,可進行圖像目標檢測,也可進行視屏和攝像檢測(pytorch框架)【python源碼+UI界面+功能源碼詳解】

功能演示&#xff1a; 基于yolov5的飛機蒙皮缺陷檢測系統&#xff0c;系統既能夠實現圖像檢測&#xff0c;也可以進行視屏和攝像實時檢測_嗶哩嗶哩_bilibili &#xff08;一&#xff09;簡介 基于yolov5的飛機蒙皮缺陷檢測系統是在pytorch框架下實現的&#xff0c;這是一個完…

單元測試:參數匹配器和參數捕捉器

目錄標題 第一章、參數匹配器1.1&#xff09;參數匹配器列表1.2&#xff09;參數匹配器示例①anyInt()②any(Class<> type)③eq()④same(expectedObject)⑤endsWith() 第二章、參數捕捉器2.1&#xff09;Captor2.2&#xff09;ArgumentCaptor類 第一章、參數匹配器 1.1&…

vue模板語法中的類和樣式綁定

最近公司要求&#xff0c;在學習前端vue知識&#xff0c;記錄一些語法要點 在綁定類和樣式時&#xff0c;如果使用三目運算(三元運算)時&#xff0c;html中使用的是大括號[], 以及使用數組綁定多個class時&#xff0c;也是大括號&#xff0c; 其他的使用中括號{} 比如&…

什么是ABA問題及ABA問題的解決方法。

什么是ABA問題 ABA問題&#xff1a;一個線程thread1讀到的數據是22&#xff0c;它對數據進行操作后變為23&#xff0c;又有一個線程thread2讀到數據22&#xff0c;對它進行操作后數據變為33并更新回去&#xff0c;又來一個線程3讀取數據33&#xff0c;更新數據為22后又更新回去…

js【詳解】自動類型轉換

運算符 Symbol 數字 會報錯 Cannot convert a Symbol value to a number Symbol 字符串 會報錯 Cannot convert a Symbol value to a string 存在對象&#xff0c;數組&#xff0c;函數時 對象&#xff0c;數組&#xff0c;函數會先執行其 toString() 方法&#xff0c;…

用CSS實現一個扇形

用CSS實現扇形的思路和三角形基本一致&#xff0c;就是多了一個圓角的樣式&#xff0c;實現一個90的扇形&#xff1a; div{border: 100px solid transparent;width: 0;heigt: 0;border-radius: 100px;border-top-color: red; }

【c++】list模擬實現

list的接口 #pragma once #include<iostream> #include<assert.h> using namespace std; namespace zjw {template<class T>struct listnode {listnode* <T>_next;listnode* <T>_prev;T _data;listnode(const T& x T()):_prev(nulllptr…

高維中介數據: 聯合顯著性(JS)檢驗法

摘要 中介分析在流行病學和臨床試驗中越來越受到關注。在現有的中介分析方法中&#xff0c;流行的聯合顯著性&#xff08;JS&#xff09;檢驗會產生過于保守的 I 類錯誤率&#xff0c;因此功效較低。但是&#xff0c;如果在使用 JS 測試高維中介假設時&#xff0c;可以準確控制…

人力資源(E-HR)應用架構的設計與實踐

當談到人力資源管理時&#xff0c;電子人力資源&#xff08;E-HR&#xff09;系統已經成為現代企業不可或缺的組成部分。E-HR系統的設計與實踐對于提高組織的人力資源管理效率和員工體驗至關重要。本文將探討E-HR應用架構的設計與實踐&#xff0c;以及如何借助信息技術優化人力…

docker清理閑置鏡像郵件發送

腳本名稱 DockerImage_Clear.sh腳本內容 #!/bin/bash # 清除閑置的docker鏡像 #docker image prune -a -f # 列出可清理的docker鏡像 /usr/local/bin/docker images | grep "<none>" # 列出可清理的docker鏡像數量 /usr/local/bin/docker images | grep …

怎么使用Pyecharts庫對淘寶數據進行可視化展示

目錄 一、準備工作 二、數據預處理 三、使用Pyecharts進行可視化展示 柱狀圖展示銷量和評價數 散點圖展示價格與銷量關系 詞云圖展示商品標題關鍵詞 四、總結與建議 在當今的大數據時代&#xff0c;數據可視化已經成為了一個非常重要的技能。Pyecharts是一個基于Python的…

NIO群聊系統的實現

一、前言 通過NIO編寫簡單版聊天室&#xff0c;客戶端通過控制臺輸入發送消息到其他客戶端。注意&#xff1a;并未處理粘包半包問題。 二、邏輯簡述 服務器&#xff1a; 1&#xff09;創建服務器NIO通道&#xff0c;綁定端口并啟動服務器 2&#xff09;開啟非阻塞模式 3&…

C++ //練習 10.24 給定一個string,使用bind和check_size在一個int的vector中查找第一個大于string長度的值。

C Primer&#xff08;第5版&#xff09; 練習 10.24 練習 10.24 給定一個string&#xff0c;使用bind和check_size在一個int的vector中查找第一個大于string長度的值。。 環境&#xff1a;Linux Ubuntu&#xff08;云服務器&#xff09; 工具&#xff1a;vim 代碼塊 /*****…

Altium Designer 22焊接輔助工具 Interactivehtmlbom 插件使用教程

目錄 AD22 由Interactivehtmlbom 插件生成的焊接輔助圖&#xff0c;交互方式很多&#xff0c;十分方便的為我們提供便利。 介紹安裝教程&#xff1a; 前去這個網站 Interactivehtmlbom 插件&#xff0c;單擊下載zip 下載完成后找個地方解壓&#xff0c;雙擊Initablelize.bat文…