c++介紹與入門基礎(詳細總結(jié))-創(chuàng)新互聯(lián)

前言

? 關(guān)于學c++有一個很有意思的段子,網(wǎng)傳學c++只需要21天即可,前面10天學了c++基礎,然后到21天還在學習對象,接口,多態(tài)。然后學著學著就兩年了,兩年后開始可以大量編寫代碼,然后與相關(guān)程序員侃侃而談。學到10年后,開始研究物理理論。20年后開始研究生物學了。40年后,運用全部知識編程序制作了一個時空穿梭機。然后跑回到40年前,把做出21天學c++這個決定的自己殺了。

成都創(chuàng)新互聯(lián)公司成立于2013年,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務公司,擁有項目網(wǎng)站制作、網(wǎng)站建設網(wǎng)站策劃,項目實施與項目整合能力。我們以讓每一個夢想脫穎而出為使命,1280元汨羅做網(wǎng)站,已為上家服務,為汨羅各地企業(yè)和個人服務,聯(lián)系電話:18980820575

當然這個僅僅是個玩笑,但是側(cè)面也反應出學習c++不是一時半會的。有的大佬的也將學習c++分成4個層次。第一個層次,C++基礎(平平常常),。第二個層次,正確高效的使用C++ (駕輕就熟)。第三個層次,深入解讀C++ (出神入化)。第四個層次,研究C++ (返璞歸真)。它相對許多語言復雜,而且難學難精,雖然學習C++有難度,但也是相當有趣且有滿足感的。

這時候有人開始對自己選擇開始懷疑了,認為生命如此短暫,掌握技藝卻要如此長久。同時我們身邊也有很多販賣焦慮的,周圍充斥著程序員生命周期短暫的煙霧彈--30歲后就面臨這失業(yè)。其實不然,通過程浩大佬,看到文章《Is Programming Knowledge Related To Age?》這篇論文介紹到了關(guān)于年齡的看法,程浩大佬認為(1)程序員技術(shù)能力上升是可以到50歲或60歲的。(2)老程序員在獲取新技術(shù)上的能力并不比年輕的程序員差。充滿焦慮,急于求成的人只是想呆在井底思維封閉而且想走捷徑速成。這篇文獻給與我一樣正在學習,并且準備為技術(shù)和編程執(zhí)著和堅持的人。因為對所做的事情的理解越深,你就會做的越好。

目錄

前言

c++的發(fā)展史?

c++的版本

c++在的工作領(lǐng)域

操作系統(tǒng)以及大型系統(tǒng)軟件開發(fā)

服務器端開發(fā) ?

游戲開發(fā)

嵌入式和物聯(lián)網(wǎng)領(lǐng)域

數(shù)字圖像處理

人工智能

分布式應用

C++關(guān)鍵字

命名空間

實際工程應用中:?

命名空間的作用:

命名空間需求展示

命名空間定義

命名空間使用

C++輸入&輸出

c++的《hello world》

輸入&輸出說明:

輸入&輸出展示?????

std命名空間的使用慣例

缺省參數(shù)?

缺省參數(shù)概念

缺省參數(shù)分類

函數(shù)重載

函數(shù)重載概念?

C++支持函數(shù)重載的原理--名字修飾(name Mangling)

引用

引用概念

引用特性

常引用

使用場景?

傳值、傳引用效率比較

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

引用和指針的區(qū)別?


c++的發(fā)展史?

? 1925年1月1日,當時AT&T總裁,華特·基佛德(Walter Gifford)收購了西方電子公司的研究部門,成立一個叫做“貝爾電話實驗室公司”的獨立實體,后改稱貝爾實驗室。

當時美國貝爾實驗室是晶體管、激光器、太陽能電池、發(fā)光二極管、數(shù)字交換機、通信衛(wèi)星、電子數(shù)字計算機、C語言、UNIX操作系統(tǒng)、蜂窩移動通信設備等通信方向,自1925年以來,貝爾實驗室共獲得兩萬五千多項專利,現(xiàn)在,平均每個工作日獲得三項多專利。

隨著科技的創(chuàng)新,丹尼斯里奇所寫的c語言已經(jīng)不能滿足程序員的需求,因為C語言是結(jié)構(gòu)化和模塊化的語言,適合處理較小規(guī)模的程序。對于復雜的問題,規(guī)模較大的程序,需要高度的抽象和建模時,C語言則不合適。為了解決軟件危機, 20世紀80年代, 計算機 界提出了OOP(object oriented programming:面向?qū)ο?思想,支持面向?qū)ο蟮某绦蛟O計語言 應運而生。

c語言之父--丹尼斯里奇的照片

1979年,當時Bjarne Stroustrup正在準備他的博士畢業(yè)論文,他有機會使用一種叫做Simula 的語言。顧名思義,Simula語言的主要作用是仿真。Simula67是Simula語言的一種變種,被公認是首款支持面向?qū)ο蟮恼Z言。Stroustrup發(fā)現(xiàn)面向?qū)ο蟮乃枷雽τ谲浖_發(fā)非常有用,但是因Simula語言執(zhí)行效率低,其實用性不強。

不久之后,Stroustrup開始著手“C with Classes”的研發(fā)工作,“C with Classes”表明這種新語言是在C基礎上研發(fā)的,是C語言的超集。C語言以其高可移植性而廣受好評,且程序執(zhí)行速度以及底層函數(shù)的性能不受程序移植的影響,Stroustrup要做的就是將面向?qū)ο蟮乃枷胍隒語言。新語言的初始版本除了包括C語言的基本特征之外,還具備類、簡單繼承、內(nèi)聯(lián)機制、函數(shù)默認參數(shù)以及強類型檢查等特性。

1982年,本賈尼·斯特勞斯特盧普(Bjarne Stroustrup)博士在C語言的基礎上引入并擴充了面向?qū)ο蟮母拍睿l(fā)明了一 種新的程序語言。為了表達該語言與C語言的淵源關(guān)系,命名為C++。因此:C++是基于C語言而 產(chǎn)生的,它既可以進行C語言的過程化程序設計,又可以進行以抽象數(shù)據(jù)類型為特點的基于對象的程序設計,還可以進行面向?qū)ο蟮某绦蛟O計。

c++之父--本賈尼·斯特勞斯特盧普的照片? ? ? ? ? ??

c++的版本
階段內(nèi)容
C with classes類及派生類、公有和私有成員、類的構(gòu)造和析構(gòu)、友元、內(nèi)聯(lián)函數(shù)、賦值運算符 重載等
C++1.0添加虛函數(shù)概念,函數(shù)和運算符重載,引用、常量等
C++2.0更加完善支持面向?qū)ο?,新增保護成員、多重繼承、對象的初始化、抽象類、靜 態(tài)成員以及const成員函數(shù)
C++3.0進一步完善,引入模板,解決多重繼承產(chǎn)生的二義性問題和相應構(gòu)造和析構(gòu)的處 理
C++98C++標準第一個版本,絕大多數(shù)編譯器都支持,得到了國際標準化組織(ISO)和美 國標準化協(xié)會認可,以模板方式重寫C++標準庫,引入了STL(標準模板庫)
C++03C++標準第二個版本,語言特性無大改變,主要:修訂錯誤、減少多異性
C++05

C++標準委員會發(fā)布了一份計數(shù)報告(Technical Report,TR1),正式更名

C++0x,即:計劃在本世紀第一個10年的某個時間發(fā)布

C++11增加了許多特性,使得C++更像一種新語言,比如:正則表達式、基于范圍for循 環(huán)、auto關(guān)鍵字、新容器、列表初始化、標準線程庫等
C++14對C++11的擴展,主要是修復C++11中漏洞以及改進,比如:泛型的lambda表 達式,auto的返回值類型推導,二進制字面常量等
C++17在C++11上做了一些小幅改進,增加了19個新特性,比如:static_assert()的文 本信息可選,F(xiàn)old表達式用于可變的模板,if和switch語句中的初始化器等
C++20自C++11以來大的發(fā)行版,引入了許多新的特性,比如:模塊(Modules)、協(xié) 程(Coroutines)、范圍(Ranges)、概念(Constraints)等重大特性,還有對已有 特性的更新:比如Lambda支持模板、范圍for支持初始化等
C++23制定ing

通過上述不同版本,我們發(fā)現(xiàn)其實c++在更新迭代是非常慢的,而且多次更新的改進也不是很大,所以先好多公司主流使用的還是c++98和c++11。好多都是這么形容c++的,好比修房子,他基礎搭建的非常好,但是向上修建的時候就比較偷工減料了,房子上層也越修越窄。

好比迪拜--哈利法塔

現(xiàn)在大家對c++23版本也持有較大期待,因為會迎來許多程序員夢寐以求的標準網(wǎng)絡庫。c++23版本離我們快要到了,有些許朋友會感到才學其他版本就要被遺棄了。不必擔憂,出來之后還沒有穩(wěn)定,大公司還需要測試之后才能廣泛使用,所以真正到使用其實還有很久。

c++在的工作領(lǐng)域 操作系統(tǒng)以及大型系統(tǒng)軟件開發(fā)

所有操作系統(tǒng)幾乎都是C/C++寫的,許多大型軟件背后幾乎都是C++寫的,比如:

Photoshop、O?ce、JVM(Java虛擬機)等,究其原因還是性能高,可以直接操控硬件。

服務器端開發(fā) ?

后臺開發(fā):主要側(cè)重于業(yè)務邏輯的處理,即對于前端請求后端給出對應的響應,現(xiàn)在主流采 用java,但內(nèi)卷化比較嚴重,大廠可能會有C++后臺開發(fā),主要做一些基礎組件,中間件、 緩存、分布式存儲等。服務器端開發(fā)比后臺開發(fā)跟廣泛,包含后臺開發(fā),一般對實時性要求 比較高的,比如游戲服務器、流媒體服務器、網(wǎng)絡通訊等都采用C++開發(fā)的。

游戲開發(fā)

PC平臺幾乎所有的游戲都是C++寫的,比如:魔獸世界、傳奇、CS、跑跑卡丁車等,市面上 相當多的游戲引擎都是基于C++開發(fā)的,比如:Cocos2d、虛幻4、DirectX等。三維游戲領(lǐng) 域計算量非常龐大,底層的數(shù)學全都是矩陣變換,想要畫面精美、內(nèi)容豐富、游戲?qū)崟r性 搞,這些高難度需求無疑只能選C++語言。比較知名廠商:騰訊、網(wǎng)易、完美世界、巨人網(wǎng) 絡等。

嵌入式和物聯(lián)網(wǎng)領(lǐng)域

嵌入式:就是把具有計算能力的主控板嵌入到機器裝置或者電子裝置的內(nèi)部,能夠控制這些 裝置。比如:智能手環(huán)、攝像頭、掃地機器人、智能音響等。 談到嵌入式開發(fā),大家最能想到的就是單片機開發(fā)(即在8位、16位或者32位單片機產(chǎn)品或者 裸機上進行的開發(fā)),嵌入式開發(fā)除了單片機開發(fā)以外,還包含在soc片上、系統(tǒng)層面、驅(qū)動 層面以及應用、中間件層面的開發(fā)。 常見的崗位有:嵌入式開發(fā)工程師、驅(qū)動開發(fā)工程師、系統(tǒng)開發(fā)工程師、Linux開發(fā)工程 師、固件開發(fā)工程師等。 知名的一些廠商,比如:以華為、vivo、oppo、小米為代表的手機廠;以紫光展銳、樂鑫為 代表的芯片廠;以大疆、??低暋⒋笕A、CVTE等具有自己終端業(yè)務廠商;以及海爾、海 信、格力等傳統(tǒng)家電行業(yè)。 隨著5G的普及,物聯(lián)網(wǎng)(即萬物互聯(lián),)也成為了一種新興勢力,比如:阿里lot、騰訊lot、京 東、百度、美團等都有硬件相關(guān)的事業(yè)部。

數(shù)字圖像處理

數(shù)字圖像處理中涉及到大量數(shù)學矩陣方面的運算,對CPU算力要求比較高,主要的圖像處理 算法庫和開源庫等都是C/C++寫的,比如:OpenCV、OpenGL等,大名鼎鼎的Photoshop就是C++寫的。

人工智能

一提到人工智能,大家首先想到的就是python,認為學習人工智能就要學習python,這個 是誤區(qū),python中庫比較豐富,使用python可以快速搭建神經(jīng)網(wǎng)絡、填入?yún)?shù)導入數(shù)據(jù)就 可以開始訓練模型了。但人工智能背后深度學習算法等核心還是用C++寫的。

分布式應用

近年來移動互聯(lián)網(wǎng)的興起,各應用數(shù)據(jù)量業(yè)務量不斷攀升;后端架構(gòu)要不斷提高性能和并發(fā) 能力才能應對大信息時代的來臨。在分布式領(lǐng)域,好些分布式框架、文件系統(tǒng)、中間組件等 都是C++開發(fā)的。對分布式計算影響極大的Hadoop生態(tài)的幾個重量級組件:HDFS、zookeeper、HBase等,也都是基于Google用C++實現(xiàn)的GFS、Chubby、BigTable。包括分 布式計算框架MapReduce也是Google先用C++實現(xiàn)了一套,之后才有開源的java版本。

C++關(guān)鍵字

C++總計63個關(guān)鍵字,C語言32個關(guān)鍵字。如果還想回顧一下c語言的關(guān)鍵字就可以點擊看看。最開始我只需知道有哪些,后面的用法我們也會專門的仔細講。

命名空間 實際工程應用中:?

?在編寫大型工程序中,往往是由多個人共同完成的,如果大家命名時想到一起去了就會產(chǎn)生處理程序中常見的同名沖突。還有可能我們使用到的函數(shù)庫,因為有些函數(shù)我們也不會經(jīng)常使用,忘記之后也有可能會跟庫函數(shù)的命名相同,這樣在程序中就會出現(xiàn)命名沖突(即重復定義)。在C/C++中,變量、函數(shù)和后面要學到的類都是大量存在的,這些變量、函數(shù)和類的名稱將都存 在于全局作用域中,可能會導致很多沖突。這個時候就會大量使用到命名空間。

命名空間的作用:

建立了一些相互分隔的作用域,將一些全局實體分隔開來,以免產(chǎn)生名字沖突??梢愿鶕?jù)需要設置多個命名空間,每個命名空間代表一個不同的命名空間域,不同的命名空間不能同名。? ? ??

使用命名空間的目的是對標識符的名稱進行本地化, 以避免命名沖突或名字污染,namespace關(guān)鍵字的出現(xiàn)就是針對這種問題的。

命名空間需求展示

? 當我們編寫如下代碼,就會出現(xiàn)編譯報錯:error C2365: “rand”: 重定義;以前的定義是“函數(shù)” 。這個問題c語言是無法解決的,但是C++提出了namespace來解決 。

#include#includeint rand = 10;
int main()
{
	printf("%d\n", rand);
	return 0;
}
命名空間定義

定義命名空間,需要使用到namespace關(guān)鍵字,后面跟命名空間的名字,然后接一對{}即可,{}中即為命名空間的成員。命名空間的名字,一般開發(fā)中是用項目名字做命名空間名。

1.正常的命名空間定義

namespace test
{

	// 命名空間中可以定義變量/函數(shù)/類型
	int rand = 10;
	int Add(int left, int right)
	{
		return left + right;
	}
	struct Node
	{
		struct Node* next;
		int val;
	};
}

2.命名空間可以嵌套

namespace test1
{
	int rand = 10;
	int Add(int left, int right)
	{
		return left + right;
	}
	struct Node
	{
		struct Node* next;
		int val;
	};
	namespace test2
	{
		int a;
		int b;
		int Sub(int left, int right)
		{
			return left - right;
		}
	}
}

3.同一個工程中允許存在多個相同名稱的命名空間,編譯器最后會合成同一個命名空間中。

通過預處理后,文件test.h與test.c都將展開。這里用到namespace,它將不同文件的test合成一個test,這里的test就相當于有兩個兩個函數(shù)。

test.h文件

namespace test
{
	int Sub(int left, int right)
	{
		return left - right;
	}
}

test.cpp文件

#define _CRT_SECURE_NO_WARNINGS
#include#include "test.h"

namespace test
{
	int Add(int left, int right)
	{
		return left + right;
	}
}

int main()
{
	int a = 2;
	int b = 3;

	int c=test::Add(a, b);
	printf("%d", c);

	return 0;
}
命名空間使用

1.加命名空間名稱及作用域限定符

namespace N
{
	int a = 0;
	int b = 2;
}


int main()
{
	printf("%d",N:: a);
	return 0;
}

2.使用using將命名空間中某個成員引入

namespace N
{
	int a = 0;
	int b = 2;
}

using N::b;

int main()
{
	printf("%d\n", N::a);
	printf("%d\n", b);
	return 0;
}

3.? 使用using namespace 命名空間名稱引入??

namespace N
{
	int a = 0;
	int b = 2;
	int Add(int left, int right)
		{
			return left + right;
		}
}
using namespce N;

int main()
{
 ? ?printf("%d\n", N::a);
 ? ?printf("%d\n", b);
 ? ?Add(10, 20);
 ? ?return 0; ? ?
}
C++輸入&輸出

? 你還記得學習c語言時“printf”的你嗎?還記得你寫下第一個《hello world》的時候嗎?一路學過來c語言全靠它發(fā)聲,如果忘記了,那就記下當下c++的親切的問候!

c++的《hello world》
#include// std是C++標準庫的命名空間名,C++將標準庫的定義實現(xiàn)都放到這個命名空間中

using namespace std;

int main()
{

cout<<"Hello world!!!"<
輸入&輸出說明:

1.使用cout標準輸出對象(控制臺)和cin標準輸入對象(鍵盤)時,與使用printf與scanf需要包含頭文件一樣,而這里包含< iostream >頭文件以及按命名空間使用方法使用std。

2.<<是流插入運算符,>>是流提取運算符。 輸出時,選擇流插入運算符;輸入時,選擇流提取運算符。

3.cout和cin是全局的流對象,endl是特殊的C++符號,表示換行輸出,他們都包含在包含< iostream >頭文件中。

4. 使用C++輸入輸出更方便,不需要像printf/scanf輸入輸出時那樣,需要手動控制格式。C++的輸入輸出可以自動識別變量類型。

輸入&輸出展示?????

我們發(fā)現(xiàn)運用cin與cout是不需要加輸入輸出類型,那么對于控制浮點的精度問題來怎么解決呢?因為c++是包容c語言的用法的,比如當我們需要控制浮點數(shù)輸出精度,控制整形輸出進制格式?,那我們就還是可以選擇用c語言的用法。

#includeusing namespace std;
 

int main()
{
 ? int a;
 ? double b;
 ? char c;
 ? ? 
 ? // 可以自動識別變量的類型

 ? cin>>a;
 ? cin>>b>>c;
 ? ? 
 ? cout<
std命名空間的使用慣例

1. 在日常練習中,建議直接using namespace std即可,這樣就很方便。

2. using namespace std展開,標準庫就全部暴露出來了,如果我們定義跟庫重名的類型/對 象/函數(shù),就存在沖突問題。該問題在日常練習中很少出現(xiàn),但是項目開發(fā)中代碼較多、規(guī)模 大,就很容易出現(xiàn)。所以建議在項目開發(fā)中使用,像std::cout這樣使用時指定命名空間 + using std::cout展開常用的庫對象/類型等方式。

缺省參數(shù)? 缺省參數(shù)概念

? 缺省參數(shù)是聲明或定義函數(shù)時為函數(shù)的參數(shù)指定一個默認值 ,在調(diào)用該函數(shù)時,如果沒有指定實參則采用該默認值,否則使用指定的實參。

#includeusing namespace std;
void Func(int a = 0)
{
	cout<< a<< endl;
}

int main()
{

	Func(); // 沒有傳參時,使用參數(shù)的默認值
	Func(10); // 傳參時,使用指定的實參

	return 0;
}

說明:

? 使用c語言時,我們是不能給參數(shù)進行設置初始值的--語法要求;當我們使用時會出報錯,參數(shù)的初始化錯誤。但是在c++中是可以的,相當于直接給函數(shù)的參數(shù)設置了一個初始值,當調(diào)用函數(shù)不傳實參時就會得到我們設置的初始值。

缺省參數(shù)分類

1.全缺省參數(shù)

將全部參數(shù)設置初始值

#includeusing namespace std;
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();
	Func(100); 
	Func(100,200);
	Func(100,200,300);

	return 0;
}

2.半缺省參數(shù)

將一部分參數(shù)設置初始值

void Func(int a, int b = 10, int c = 20)
{
	cout<< "a = "<< a<< endl;
	cout<< "b = "<< b<< endl;
	cout<< "c = "<< c<< endl;
}

int main()
{

	//Func();//因為第一個參數(shù)沒有設置初始值,所以第一個參數(shù)需要穿實參
	Func(100); 
	Func(100,200);
	Func(100,200,300);

	return 0;
}

注意:

1. 半缺省參數(shù)必須從右往左依次來給出,不能間隔著給

2. 缺省值必須是常量或者全局變量

3. C語言不支持(編譯器不支持)

4. 缺省參數(shù)不能在函數(shù)聲明和定義中同時出現(xiàn)

第4點說明

//a.h文件

 ?void Func(int a = 10);
 ?
 ?// a.cpp文件

 ?void Func(int a = 20)
 {}
 ?
 ?// 注意:如果生命與定義位置同時出現(xiàn),恰巧兩個位置提供的值不同,那編譯器就無法確定到底該
用那個缺省值。
函數(shù)重載

? 函數(shù)重載跟我們生活中一詞多意很像,人們可以通過上下文或者語境來判斷該詞真實的含義,即該詞被重載了。好比如你喜歡的女孩對你說:你很好!或者成為女朋友了對你說:我很好!

函數(shù)重載概念?

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

1.參數(shù)類型不同?

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()
{
 Add(10, 20);
 Add(10.1, 20.2);
 return 0;
}

2、參數(shù)個數(shù)不同

void f()
{
	cout<< "f()"<< endl;
}

void f(int a)
{
	cout<< "f(int a)"<< endl;
}

int main()
{
	f();
	f(10);
    return 0;
}

3、參數(shù)類型順序不同

void f(int a, char b)
{
	cout<< "f(int a,char b)"<< endl;
}

void f(char b, int a)
{
	cout<< "f(char b, int a)"<< endl;
}

int main()
{
	f(10, 'a');
	f('a', 10);
	return 0;
}

C++支持函數(shù)重載的原理--名字修飾(name Mangling)

在這里我們必須知道這幾過程:預處理、編譯、匯編、鏈接。重拾:c--程序環(huán)境與預處理。首先我們知道預處理是將各個文件代碼展開,然后把該替換的替換了,該刪除的刪除了。然后進入編譯階段,將c語言代碼轉(zhuǎn)換成匯編指針,在這過程中有詞法分析,語法分析等。再這就匯編階段,它將匯編代碼轉(zhuǎn)換為計算機認識的二進制指令,這里重點就是會生成符號表,符號表中有函數(shù)名和地址。

下面就是編譯階段,在linux下gcc環(huán)境和g++環(huán)境編譯完成階段的兩端代碼。

采用C語言編譯器編譯后結(jié)果 :

這里我們仔細觀察得到,在linux下,采用gcc編譯完成后,這里的函數(shù)名沒有任何修飾,如果我們用兩個相同的函數(shù)名,編譯器是無法辨別的。

采用C++編譯器編譯后結(jié)果:

那么在linux下,采用g++編譯完成后,我們發(fā)現(xiàn)函數(shù)是得到修飾的。編譯器將函數(shù)參 數(shù)類型信息添加到修改后的名字中。這里好比兩個紅玫瑰蘋果,一個是紅玫瑰125克,一個是紅玫瑰124克。他們都可以裝在一個蘋果籃子里,但是都能取分開。

我們在c++環(huán)境下,通過對函數(shù)名字的修飾((name Mangling),讓相同的函數(shù)不同的功能得以實現(xiàn)。只要參數(shù)不同,修飾出來的名字就不一樣,就支持了重載。但是不同系統(tǒng)函數(shù)修飾規(guī)則是有不同的。

在最后鏈接階段,通過連接器(Linker)將所有二進制形式的目標文件和系統(tǒng)組件組合成一個可執(zhí)行文件。

引用 引用概念

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

這里就比好:蔡徐坤,在籃球上粉絲都愛稱:“雞,你太美”。其他方面上,粉絲親切叫一聲:“哥哥或者坤坤”。

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

#includevoid TestRef()
{
	int a = 10;
	int& ra = a;//<====定義引用類型

	printf("%p\n", &a);
	printf("%p\n", &ra);
}

int main()
{
	TestRef();

	return 0;
}

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

引用特性

1. 引用在定義時必須初始化

2. 一個變量可以有多個引用

3. 引用一旦引用一個實體,再不能引用其他實體

void TestRef()
{
	int a = 10;
	// int& ra; ? // (1)該條語句編譯時會出錯
	int b = 20;


	int& ra = a;
	int& ra = b;//(3)報錯:重定義;多次初始化
	int& rra = a;
	printf("%p %p %p\n", &a, &ra, &rra);
}

int main()
{
	TestRef();

	return 0;
}
常引用

const 類型 & 引用名;

作用:是不希望對所引用的內(nèi)容進行修改。

void TestConstRef()
{
	const int a = 10;
	//int& ra = a; ? //(1) 該語句編譯時會出錯,a為常量

	const int& ra = a;

	// int& b = 10; //(2) 該語句編譯時會出錯,b為常量
	const int& b = 10;

	double d = 12.34;
	//int& rd = d; // (3)該語句編譯時會出錯,類型不同

	const int& rd = d;
}

解釋:

出錯1:

a被const修飾,a的權(quán)限被縮小,而這里卻想將a的權(quán)限擴大,在操作系統(tǒng)中權(quán)限只能縮小,不能被擴大,解決辦法const int& ra=a;

出錯2:

引用的值必須為左值,b為常量。常量是一個真實值,可讀,所以b為右值。那么左值相當于地址。因為這里b是在進行初始化,是一個臨時對象。所以這里錯誤就是右值引用。右值進行引用的時候需要被const修飾,const int& b = 10;

右值引用簡單理解,就是綁定到左值的引用,右值引用的特點是:它將讓其所綁定的右值重獲新生,即使該右值本身是一個臨時變量,但是它本身卻不能綁定任何左值。

錯誤3:

d為double類型,引用不能改變類型。那么這里加const讓rd變成可讀,相當于對d建立臨時變量,以前我們遇到的截斷,提升都屬于通建立臨時變量解決的問題的。

運用:

void func(const int& b)
{
	cout<< "b:"<< &b<< endl;
	int c = b;
	cout<< "c:"<< &c<< endl;
	c++;
	cout<< "c value"<< c<< endl;

}

int main()
{

	int a = 10;
	cout<< "a:"<< &a<< endl;
	func(a);

	return 0;
}

可以看出a和b的地址一樣,而c與b的地址不一樣,可以修改c的值,但是無法修改b的內(nèi)容。

使用場景?

1.做參數(shù)

void Swap(int& left, int& right)
{
	int temp = left;
	left = right;
	right = temp;
}


int main()
{
	int a = 5, b = 6;

	Swap(a,b);
	cout<< a<<" "<< b<< endl;

	return 0;
}

以前我們學習c語言是用的指針,現(xiàn)在我們學習c++就可以用引用替代,而且書寫代碼更加簡潔。?

2.做返回值

這里引用做返回值使用是就需要特別小心,因為會發(fā)生函數(shù)棧幀,當函數(shù)被銷毀的時候,他原來的空間就會被覆蓋,那么有可能會我們用得到的返回值,再去進行訪問時就找到原來那個值。

列如:

這里使用static,返回值n是靜態(tài)變量,存放的空間是在全局區(qū)(代碼區(qū))而不是棧區(qū)。

int& Count()
{
 ? static int n = 0;
 ? n++;
 ? // ...

 ? return n;
}

那么使用下面代碼會發(fā)生什么呢?

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<

我們發(fā)現(xiàn)第二次使用的Add函數(shù)時,ret的結(jié)果發(fā)生了改變。這里主要是因為兩次都是使用的Add函數(shù),開辟的空間是一樣大,所以第一次使用函數(shù)的空間被第二次使用函數(shù)的空間所覆蓋。原本是3的值,第二次進行訪問的時候原位置是7了。

如果想象不出這個過程,通過圖來理解:

我們這里只是一種情況,其實它有三種情況:(1)原來的值 (2)隨機值 (3)被覆蓋的值

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;
	cout<< "Add(1, 2) is :"<< ret<< endl;
	Add(3, 4);
	cout<< "Add(1, 2) is :"<< ret<< endl;
	return 0;
}

那么如何理解這三種情況呢?

其實很好理解,因為太貼近我們生活了。當你一個人去開房,走的時候發(fā)現(xiàn)你華為手機掉在房間了,然后你回去找,第一種情況:你找到了你自己的手機。第二種情況:阿姨打掃了,原來的地方?jīng)]有你的手機,可能放在前臺了。第三中情況:別人已經(jīng)入住了,你原來放手機的位置放的是蘋果手機(別人的)。

我們要記住這個是錯誤代碼展示,只是讓我們深入理解之后不在編寫出這樣的代碼。

正確代碼:

//方法一
int Add(int a, int b)
{
	int c = a + b;
	return c;
}
//方法二
int& Add(int a, int b)
{
	static int c = a + b;
	return c;
}

結(jié)論:

如果函數(shù)返回時,出了函數(shù)作用域,如果返回對象還在(還沒還給系統(tǒng)),則可以使用引用返回,如果已經(jīng)還給系統(tǒng)了,則必須使用傳值返回。

傳值、傳引用效率比較

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

#includestruct A{ 
	int a[10000];
};
void TestFunc1(A a){
}

void TestFunc2(A& a){
}

void TestRefAndValue(){
	A a;    // 以值作為函數(shù)參數(shù)    
	size_t begin1 = clock();  
	for (size_t i = 0; i< 10000; ++i)  
		TestFunc1(a);   
	size_t end1 = clock();
	// 以引用作為函數(shù)參數(shù)   
	size_t begin2 = clock();
	for (size_t i = 0; i< 10000; ++i)    
		TestFunc2(a); 
	size_t end2 = clock();
	// 分別計算兩個函數(shù)運行結(jié)束后的時間  
	cout<< "TestFunc1(A)-time:"<< end1 - begin1<< endl;  
	cout<< "TestFunc2(A&)-time:"<< end2 - begin2<< endl;
}

int main()
{
	TestRefAndValue();

	return 0;
}

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

傳值和指針在作為傳參以及返回值類型上效率相差很大。

#includestruct A{ int a[10000]; };

A a;// 值返回
A TestFunc1() {
	return a;
}
// 引用返回
A& TestFunc2(){
	return a;
}
void TestReturnByRefOrValue(){  
	// 以值作為函數(shù)的返回值類型    
	size_t begin1 = clock();    
	for (size_t i = 0; i< 100000; ++i)      
		TestFunc1();  
	size_t end1 = clock();
	// 以引用作為函數(shù)的返回值類型   
	size_t begin2 = clock();  
	for (size_t i = 0; i< 100000; ++i) 
		TestFunc2();   
	size_t end2 = clock();
	// 計算兩個函數(shù)運算完成之后的時間   
	cout<< "TestFunc1 time:"<< end1 - begin1<< endl;  
	cout<< "TestFunc2 time:"<< end2 - begin2<< endl;
}

int main()
{
	TestReturnByRefOrValue();

	return 0;
}

引用和指針的區(qū)別?

在語法概念上引用就是一個別名,沒有獨立空間,和其引用實體共用同一塊空間。

int main(){
	int a = 10; 
	int& ra = a;
	cout<< "&a = "<< &a<< endl; 
	cout<< "&ra = "<< &ra<< endl;
	return 0;
}

在語法上,他們的地址都是一樣的,所以共用一塊空間。但是在底層實現(xiàn)上實際是有空間的,因為引用是按照指針方式來實現(xiàn)的。

int main(){
	int a = 10;
	int& ra = a; 
	ra = 20;
	int* pa = &a;
	*pa = 20;
	return 0;
}

引用和指針的語法對比圖:

我們來看下引用和指針的匯編代碼對比:

在底層引用與指針,當變量很小的時候都是用寄存器存儲的,相當于開辟一塊臨時變量。?

引用和指針的不同點:

1. 引用概念上定義一個變量的別名,指針存儲一個變量地址。

2. 引用在定義時必須初始化,指針沒有要求

3. 引用在初始化時引用一個實體后,就不能再引用其他實體,而指針可以在任何時候指向任何一個同類型實體

4. 沒有NULL引用,但有NULL指針

5. 在sizeof中含義不同:引用結(jié)果為引用類型的大小,但指針始終是地址空間所占字節(jié)個數(shù)(32

位平臺下占4個字節(jié))

6. 引用自加即引用的實體增加1,指針自加即指針向后偏移一個類型的大小

7. 有多級指針,但是沒有多級引用

8. 訪問實體方式不同,指針需要顯式解引用,引用編譯器自己處理

9. 引用比指針使用起來相對更安全

內(nèi)聯(lián)函數(shù)

? 在c語言中,我們?yōu)榱私鉀Q調(diào)用函數(shù)不開辟棧幀,我們經(jīng)常會使用到宏,但是使用宏是有缺陷的,1不能調(diào)試,2沒有類型安全檢查,3容易寫錯。為了解決這些問題c++就使用內(nèi)聯(lián)函數(shù)。

宏與內(nèi)聯(lián)的比較
#include#define Add(x,y) ((x)+(y))

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

int main()
{
	int x = 10, y = 20;
	int ret = Add(x, y);
	int ret2 = add(x, y);

	printf("%d\n",ret);
	printf("%d\n", ret2);

	return 0;
}
概念

以inline修飾的函數(shù)叫做內(nèi)聯(lián)函數(shù),編譯時C++編譯器會在調(diào)用內(nèi)聯(lián)函數(shù)的地方展開,沒有函數(shù)調(diào) 用建立棧幀的開銷,內(nèi)聯(lián)函數(shù)提升程序運行的效率。

如果在上述函數(shù)前增加inline關(guān)鍵字將其改成內(nèi)聯(lián)函數(shù),在編譯期間編譯器會用函數(shù)體替換函數(shù)的 調(diào)用。

查看方式:?

1. 在release模式下,查看編譯器生成的匯編代碼中是否存在call Add

2. 在debug模式下,需要對編譯器進行設置,否則不會展開(因為debug模式下,編譯器默認不 會對代碼進行優(yōu)化,以下給出vs2013的設置方式)

特性

1. inline是一種以空間換時間的做法,如果編譯器將函數(shù)當成內(nèi)聯(lián)函數(shù)處理,在編譯階段,會 用函數(shù)體替換函數(shù)調(diào)用,缺陷:可能會使目標文件變大,優(yōu)勢:少了調(diào)用開銷,提高程序運行效率。

展開會用更多的寄存器,空間就會變大,這樣就會影響可執(zhí)行程序的大小--安裝包。

2. inline對于編譯器而言只是一個建議,不同編譯器關(guān)于inline實現(xiàn)機制可能不同,一般建議:將函數(shù)規(guī)模較小(即函數(shù)不是很長,具體沒有準確的說法,取決于編譯器內(nèi)部實現(xiàn))、不是遞歸、且頻繁調(diào)用的函數(shù)采用inline修飾,否則編譯器會忽略inline特性。

當函數(shù)長了之后展開會發(fā)聲代碼膨脹

3. inline不建議聲明和定義分離,分離會導致鏈接錯誤。因為inline被展開,就沒有函數(shù)地址 了,鏈接就會找不到。 ?

// F.h

#includeusing namespace std;

inline void f(int i);

// F.cpp

#include "F.h"

void f(int i)
{
 cout<< i<< endl;
}

// main.cpp

#include "F.h"

int main()
{
 f(10);
 return 0;
}

// 鏈接錯誤:main.obj : error LNK2019: 無法解析的外部符號 "void __cdecl 
f(int)" (?f@@YAXH@Z),該符號在函數(shù) _main 中被引用
auto關(guān)鍵字?

隨著程序越來越復雜,程序中用到的類型也越來越復雜,經(jīng)常體現(xiàn)在:

1. 類型難于拼寫

2. 含義不明確導致容易出錯

#include#includeint main()
{
 std::mapm{ { "apple", "蘋果" }, { "orange", 

"橙子" }, 
 ? {"pear","梨"} };
 std::map::iterator it = m.begin();
 while (it != m.end())
 {
 //....

 }
 return 0;
}

std::map::iterator 是一個類型,但是該類型太長了,特別容 易寫錯。聰明的同學可能已經(jīng)想到:可以通過typedef給類型取別名,比如: ?

#include #include

typedef std::mapstd::string, std::string>Map;

int main() {
 Map m{ 
           {
                 "apple", "蘋果" },{ "orange", "橙子" }, {"pear","梨"} 
};

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

auto簡介

在早期C/C++中auto的含義是:使用auto修飾的變量,是具有自動存儲器的局部變量,但遺憾的 是一直沒有人去使用它,大家可思考下為什么?

C++11中,標準委員會賦予了auto全新的含義即:auto不再是一個存儲類型指示符,而是作為一 個新的類型指示符來指示編譯器,auto聲明的變量必須由編譯器在編譯時期推導而得。

int TestAuto()
{
 return 10;
}

int main()
{
 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;
 
 //auto e; 無法通過編譯,使用auto定義變量時必須對其進行初始化

 return 0;
}

【注意】

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

auto的使用細則

1. auto與指針和引用結(jié)合起來使用 用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;
}

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

void TestAuto()
{
 ? ?auto a = 1, b = 2; 
 ? ?auto c = 3, d = 4.0; ?// 該行代碼會編譯失敗,因為c和d的初始化表達式類型不同

}
auto不能推導的場景

1. auto不能作為函數(shù)的參數(shù)

// 此處代碼編譯失敗,auto不能作為形參類型,因為編譯器無法對a的實際類型進行推導

void TestAuto(auto a)
{}

2. auto不能直接用來聲明數(shù)組 ?

void TestAuto()
{
 ? ?int a[] = {1,2,3};
 ? ?auto b[] = {4,5,6};
}

3. 為了避免與C++98中的auto發(fā)生混淆,C++11只保留了auto作為類型指示符的用法

4. auto在實際中最常見的優(yōu)勢用法就是跟以后會講到的C++11提供的新式for循環(huán),還有

lambda表達式等進行配合使用。

基于范圍的for循環(huán)(C++11) 范圍for的語法

在C++98中如果要遍歷一個數(shù)組,可以按照以下方式進行:

void TestFor()
{
 int array[] = { 1, 2, 3, 4, 5 };
 for (int i = 0; i< sizeof(array) / sizeof(array[0]); ++i)
 ? ? array[i] *= 2;
 
 for (int* p = array; p< array + sizeof(array)/ sizeof(array[0]); ++p)
 ? ? cout<< *p<< endl;
}

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

void TestFor()
{
 int array[] = { 1, 2, 3, 4, 5 };
 for(auto& e : array)
 ? ? e *= 2;
 
 for(auto e : array)
 ? ? cout<< e<< " ";
 
 return 0;
}

注意:

與普通循環(huán)類似,可以用continue來結(jié)束本次循環(huán),也可以用break來跳出整個循環(huán)。?

范圍for的使用條件?

1. for循環(huán)迭代的范圍必須是確定的 對于數(shù)組而言,就是數(shù)組中第一個元素和最后一個元素的范圍;對于類而言,應該提供begin和end的方法,begin和end就是for循環(huán)迭代的范圍。

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

void TestFor(int array[])
{
 ? ?for(auto& e : array)
 ? ? ? ?cout<< e<

2. 迭代的對象要實現(xiàn)++和==的操作。(關(guān)于迭代器這個問題,以后會講,現(xiàn)在提一下,沒辦法 講清楚,現(xiàn)在大家了解一下就可以了)

指針空值nullptr(C++11) C++98中的指針空值

在良好的C/C++編程習慣中,聲明一個變量時最好給該變量一個合適的初始值,否則可能會出現(xiàn) 不可預料的錯誤,比如未初始化的指針。如果一個指針沒有合法的指向,我們基本都是按照如下 方式對其進行初始化:

void TestPtr()
{
 int* p1 = NULL;
 int* p2 = 0;
 
 // ……

}

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

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

可以看到,NULL可能被定義為字面常量0,或者被定義為無類型指針(void*)的常量。不論采取何 種定義,在使用空值的指針時,都不可避免的會遇到一些麻煩,比如:

void f(int)
{
 cout<<"f(int)"<

注意:

1. 在使用nullptr表示指針空值時,不需要包含頭文件,因為nullptr是C++11作為新關(guān)鍵字引入 的。

2. 在C++11中,sizeof(nullptr) 與 sizeof((void*)0)所占的字節(jié)數(shù)相同。

3. 為了提高代碼的健壯性,在后續(xù)表示指針空值時建議最好使用nullptr。

今天的知識到這里就完了,希望該文章能對各位朋友有一絲幫助!? ? ? ? ? ? ? ? ? ? ??? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (全文完)?

你是否還在尋找穩(wěn)定的海外服務器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調(diào)度確保服務器高可用性,企業(yè)級服務器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧

當前名稱:c++介紹與入門基礎(詳細總結(jié))-創(chuàng)新互聯(lián)
網(wǎng)站地址:http://muchs.cn/article38/cdcjsp.html

成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站維護、響應式網(wǎng)站、外貿(mào)網(wǎng)站建設、定制開發(fā)小程序開發(fā)、網(wǎng)站排名

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)