目錄:
經(jīng)過了很長時間對C語言和c的數(shù)據(jù)結(jié)構(gòu)學(xué)習(xí),終于開始學(xué)C++了。前面基礎(chǔ)不牢,后面地動山搖,給自己加油。
該如何去學(xué)習(xí)c++?
多寫博客。寫博客,主要是總結(jié)自己學(xué)習(xí)的知識,更系統(tǒng)更全面對所學(xué)的知識進(jìn)行概況,也能起到復(fù)習(xí)的作用。
有時間的話多看看課外書
c++的學(xué)習(xí)不是一年、兩年能學(xué)會的,要把學(xué)習(xí)語言作為一個持續(xù)的過程。
寫代碼能力很重要,多去??途W(wǎng)和leetcode去刷題。
這篇博客寫的是:
C語言語法的不足,以及c++在C語言設(shè)計(jì)不合理的地方進(jìn)行優(yōu)化,為以后的c++學(xué)習(xí)奠定基礎(chǔ)。
c++是貝爾實(shí)驗(yàn)室的本賈尼等人在C語言的基礎(chǔ)上進(jìn)行擴(kuò)展,增加了類的機(jī)制,稱為c with classes。c++是面向?qū)ο蟮恼Z言,它包含c。
一、c++關(guān)鍵字(c++98)
c++63個關(guān)鍵字,c語言32個關(guān)鍵字
等c++學(xué)完后,我會單獨(dú)總結(jié)關(guān)鍵字。
二、命名空間
命名空間解決C語言的命名沖突問題
2.1 命名空間的定義
如:
rand重定義了,以前是函數(shù),C語言沒有辦法解決這個命名沖突問題,c++提出了用namespace來解決
下面就是命名空間的定義
pp是命名空間的名字,命名空間中可以定義/變量/函數(shù)/類型。
命名空間可以嵌套使用
同一個工程文件可以存在多個相同名稱的命名空間,編譯器最后會合成同一個命名空間中(如頭文件的聲明給它封起來,源文件的定義也給它封起來,因?yàn)樗鼈兊拿臻g相同,所以最后會合成為同一個命名空間)
注意 :一個命名空間就定義了一個新的作用域,命名空間的所有內(nèi)容都局限于該命名空間中。
2.2 命名空間的使用
命名空間的使用有三種方式:
- 加命名空間名稱以及作用域限定符
N::a - 使用using將命名空間中某個成員引用
using N::a; - 使用using namespace 命名空間名稱引用
using namespace N;
三、c++輸入與輸出
解釋:
1.使用cout標(biāo)準(zhǔn)輸出對象(控制臺)和cin標(biāo)準(zhǔn)輸入對象(鍵盤)時,必須包含頭文件以及按命名空間使用方法使用std。
2.cout和cin是全局的流對象,endl是特殊的c++符號,表示換行輸出。
3.<<流插入運(yùn)算符,>>流提取運(yùn)算符。
4。使用c++輸入輸出更方便,不需要C語言那樣,手動控制格式,c++自動識別變量類型。
#include<iostream>
using namespace std;//使用c++標(biāo)準(zhǔn)庫的命名空間std,c++定義實(shí)現(xiàn)都放到這個命名空間里
int main()
{
int a;
float b;
char c;
//自動識別變量的類型
cin >> a;
cin >> b >>c;
//流輸出
cout << a<<" "<< b <<" "<< c << endl;
return 0;
}
std命名空間的使用:
1.日常生活中直接使用 using namespace std即可,這樣方便使用。
2.using namespace std展開,標(biāo)準(zhǔn)庫就全部暴露出來了,如果我們的定義和庫有重名,就會存在沖突問題。日常練習(xí)很少出現(xiàn)問題,所以可以展開;項(xiàng)目開發(fā)的時候,代碼很多,就容易出現(xiàn)沖突問題,在項(xiàng)目開發(fā)過程中,我們可以std::cout這樣使用指定命名空間+using std::cout 展開常用的庫對象和類型等。。
四、缺省參數(shù)
缺省參數(shù)就是聲明和定義函數(shù)的時候?yàn)楹瘮?shù)的參數(shù)指定一個缺省值。調(diào)用該函數(shù)時,如果沒有指定實(shí)參,就會采用該形參的缺省值,否則使用該實(shí)參。
- 全缺省參數(shù)
- 半缺省參數(shù)
參考:
注意:
1.半缺省參數(shù)必須從右往左連續(xù)給值,不能間隔給。
2.缺省參數(shù)不能在函數(shù)聲明和定義中同時出現(xiàn)。
3.缺省參數(shù)必須是常量或者全局變量。
4.C語言不支持(編譯器不支持)。
五、函數(shù)重載
函數(shù)重載: 同一個作用域中聲明的功能相似的同名函數(shù),這些同名函數(shù)的形參列表(參數(shù)個數(shù)或類型或順序)都不同。
- 參數(shù)類型不同
- 參數(shù)個數(shù)不同
- 參數(shù)類型順序不同
參考:
#include<iostream>
using namespace std;
//1.參數(shù)類型不同
int Add(int a, int b)
{
cout << "int Add(int a, int b)" << endl;
return a + b;
}
double Add(double a, double b)
{
cout << "double Add(double a, double b)" << endl;
return a + b;
}
//2.參數(shù)個數(shù)不同
void f(int a, int b)
{
cout << "int f(int a, int b)" << endl;
cout << a + b << endl;
}
void f(int a)
{
cout << "int f(int a)" << endl;
cout << a << endl;
}
//3.參數(shù)類型順序不同
void f(int a, char b)
{
cout << "void f(int a, char b)" << endl;
cout << a <<b<< endl;
}
void f(char a, int b)
{
cout << "void f(char a, int b)" << endl;
cout << a << b << endl;
}
int main()
{
cout<< Add(10, 20)<<endl;
cout << Add(10.1, 20.2)<<endl;
f(5,5) ;
f(5) ;
f(1, 'a');
f('a', 1);
return 0;
}
為什么c++支持函數(shù)重載,而C語言不支持函數(shù)重載?
c/c++程序要想運(yùn)行起來必須經(jīng)歷:預(yù)編譯、編譯、匯編、鏈接這幾個階段。符號表里有函數(shù)名和它的地址,C語言鏈接函數(shù)地址時,就用函數(shù)名去找(C語言不存在同名函數(shù));c++是通過函數(shù)修飾規(guī)則來區(qū)分的,只要參數(shù)不同,修飾出來的名字就不一樣,就支持重載。
舉例:
在LINUX系統(tǒng)環(huán)境下,gcc的函數(shù)修飾后不變,而g++函數(shù)修飾后變成【_z+函數(shù)長度+函數(shù)名+類型首字母】
采用C語言編譯器編譯后的結(jié)果:
結(jié)論:在linux系統(tǒng)下,用gcc編譯完成后,函數(shù)名字沒有發(fā)生改變
采用c++編譯器編譯的結(jié)果:
結(jié)論:在linux系統(tǒng)下,用g++編譯完成后,函數(shù)名字發(fā)生改變了。
注意:
兩個函數(shù)的函數(shù)名和參數(shù)一樣,返回值不同是構(gòu)不成重載的,因?yàn)檎{(diào)用時編譯器沒辦法區(qū)分。
六、引用
類型& 引用變量名(對象名)=引用實(shí)體
引用不是新定義一個變量,是給已經(jīng)存在的變量取個別名,編譯器不會為引用開辟一塊空間,它和它引用的那個變量共用一塊內(nèi)存空間。
注意: 引用類型必須和引用實(shí)體是同種類型。
6.1引用特性
1.引用定義時,必須初始化
2.一個變量可以有多個引用
3.引用一旦引用一個實(shí)體,就不能引用其他實(shí)體。
6.2常引用
#include <iostream>
using namespace std;//把標(biāo)準(zhǔn)庫展開一下
void test_1()
{
const int a = 10;
// int& ra = a;//a具有常屬性,ra沒有常屬性,不能給a取別名,權(quán)限的變大
const int b = 20;
const int& rb = b;//可以對常量進(jìn)行引用,前提是變量具有常屬性,權(quán)限的保持
const int& bb = 20;//可以對常量進(jìn)行引用,前提是變量具有常屬性,權(quán)限的保持
double c = 3.14;
const double& rc = c;//權(quán)限的縮小
int d= 30;
//double& rd = d;//不能給類型不同的數(shù)據(jù)取別名
cout << rb << " " << bb << " " << rc << endl;
}
int main()
{
test_1();
return 0;
}
算術(shù)轉(zhuǎn)換,整型提升,在賦值/引用的時候都會產(chǎn)生一個臨時變量,這個臨時變量具有常屬性。
6.3使用場景
- 做參數(shù)
- 做返回值
#include <iostream>
using namespace std;//把標(biāo)準(zhǔn)庫展開一下
//1.做參數(shù)
void test_1(int& a,int& b)
{//在函數(shù)中讓值做交換
int tem = a;
a = b;
b = tem;
}
//2.做返回值
int& test_2(int a,int b)
{//返回的對象在靜態(tài)區(qū),函數(shù)棧幀結(jié)束,對象還在。
static int c = a + b;//static修飾的變量n在靜態(tài)區(qū)中,靜態(tài)區(qū)只能被初始化一次,后面就不能初始化了
return c;
}
int main()
{
int x = 10, y = 20, z = 30, w = 40;
test_1(x,y);
int& add=test_2(z,w);
test_2(10, 20);
cout << x << " " << y << " " << add<< endl;//輸出20 10 70
return 0;
}
注意:如果函數(shù)返回時,出了函數(shù)作用域,返回的那個對象還在(沒有還給系統(tǒng)),就可以使用返回值了;如果返回的那個對象不在(已經(jīng)返回給系統(tǒng)),就必須使用傳值返回,要是使用引用返回,結(jié)果就是未定義得(可能對,也可能錯)。
6.4傳值和傳引用效率比較
以值作為參數(shù)或者返回值類型,在傳參和返回期間,函數(shù)不會直接傳遞實(shí)參或者將變量本身直接返回,而是傳遞實(shí)參或者返回變量的一份臨時的拷貝,因此用值作為參數(shù)或者返回值類型,效率是非常低下的,尤其是當(dāng)參數(shù)或者返回值類型非常大時,效率就更低。
引用的作用就是為了提高效率。
1.引用做參數(shù)的比較
#include <iostream>
#include <time.h>
using namespace std;//把標(biāo)準(zhǔn)庫展開一下
//定義一個結(jié)構(gòu)體
struct C
{
int a[100];
};
void A(C& c)
{}
void B(C c)
{}
int main()
{
C c;
int i;
//引用做參數(shù)
int begin1 = clock();
for (i = 0; i < 100000;i++)
A(c);
int end11 = clock();
//傳值做參數(shù)
int begin2 = clock();
for (i = 0; i < 100000; i++)
B(c);
int end22 = clock();
cout << "A(c)-time:" <<end11- begin1<< endl;
cout << "B(c)-time:" << end22-begin2 <<endl;
return 0;
}
2.引用做返回值的比較
#include <iostream>
#include <time.h>
using namespace std;//把標(biāo)準(zhǔn)庫展開一下
//定義一個結(jié)構(gòu)體
struct C
{
int a[100];
}c;
C& A()
{
return c;
}
C B()
{
return c;
}
int main()
{
int i;
//引用返回
int begin1 = clock();
for (i = 0; i < 100000; i++)
A();
int end11 = clock();
//傳值返回
int begin2 = clock();
for (i = 0; i < 100000; i++)
B();
int end22 = clock();
cout << "A()-time:" << end11 - begin1 << endl;
cout << "B()-time:" << end22 - begin2 << endl;
return 0;
}
通過上面比較,我們發(fā)現(xiàn)引用在作為函數(shù)的參數(shù)和返回值類型效率比傳值的效率高。
6.5引用和指針的區(qū)別
在語法概念上引用就是一個別名,沒有獨(dú)立空間,和其引用實(shí)體共用同一塊空間。
在底層實(shí)現(xiàn)上實(shí)際是有空間的,因?yàn)橐檬前凑罩羔樀姆绞饺?shí)現(xiàn)的(引用和指針的匯編代碼一樣)。
需要牢牢記住
引用和指針的不同點(diǎn):
1.引用概念上是定義一個變量的別名,指針存儲的是一個變量地址。
2.引用在定義上必須初始化,指針沒有要求。
3.引用對一個實(shí)體初始化過后,就不能在引用其他的實(shí)體了,而指針可以在任何時候指向任何一個同類型實(shí)體。
4.沒有NULL引用,但有NULL指針。
5.在sizeof中含義不同:引用結(jié)果為引用類型的大小,但指針始終是地址空間所占的字節(jié)個數(shù)(32位平臺下占4個字節(jié))
6.引用自加即引用的實(shí)體增加1,指針自加即指針向后偏移一個類型的大小。
7.有多級指針,但是沒有多級引用。
8.訪問實(shí)體方式不同,指針需要解引用操作,引用編譯器自己處理。
9.引用比指針使用起來更加安全。
七、內(nèi)聯(lián)函數(shù)
必須掌握:
宏的優(yōu)缺點(diǎn):
優(yōu)點(diǎn):
1.增強(qiáng)代碼的復(fù)用性。
2.提高性能。
缺點(diǎn):
1.不能調(diào)試(預(yù)編譯階段進(jìn)行了替換)
2.宏沒有類型,比較危險
3.代碼可讀性差,可維護(hù)性差,容易誤用。
c++用那些技術(shù)代替宏:
故c++推薦:const和enum替換宏常量,iline替換宏函數(shù)(不用建立棧幀,把宏的缺點(diǎn)全部去掉,inline會對函數(shù)進(jìn)行優(yōu)化,debug環(huán)境下,需要進(jìn)行配置,內(nèi)聯(lián)函數(shù)是個建議)
7.1 概念
inline修飾的函數(shù)也叫內(nèi)聯(lián)函數(shù),編譯時c++編譯器會在調(diào)用內(nèi)聯(lián)函數(shù)的地方展開,不需要函數(shù)調(diào)用建立棧幀的開銷,內(nèi)聯(lián)函數(shù)提升運(yùn)行效率。
案例:
在debug模式下,F(xiàn)10來調(diào)試,然后進(jìn)行反匯編。add函數(shù)建立一個函數(shù)棧幀,然后通過call 跳轉(zhuǎn)到add函數(shù)的地址,去訪問那塊空間。
在debug模式下,需要對編譯器進(jìn)行設(shè)置,inline內(nèi)聯(lián)函數(shù)才會展開(debug模式下編譯器不會對代碼進(jìn)行優(yōu)化)
1.右擊彈出菜單
2.點(diǎn)擊屬性
3.按圖操作,快速掌握
下面就是設(shè)置完成后,進(jìn)行debug調(diào)試,反匯編的代碼,我們可以看到,內(nèi)聯(lián)函數(shù)直接在調(diào)用的地方展開,極大的提高了程序運(yùn)行的效率。
7.2特性
1.inline是以空間換時間的做法。缺點(diǎn):編譯出來的指令多,目標(biāo)文件會變大;優(yōu)點(diǎn):少了調(diào)用的開銷,提高了程序運(yùn)行效率。指令影響的是可執(zhí)行程序的大小。
2.inline只是一個建議,不同編譯器關(guān)于inline實(shí)現(xiàn)機(jī)制可能不同。它適合函數(shù)規(guī)模小,并且不能遞歸,且頻繁調(diào)用的函數(shù)。(一般10行以內(nèi)的可以用內(nèi)聯(lián)函數(shù),內(nèi)聯(lián)只是向編譯器發(fā)出一種請求,編譯器可以忽略這個請求,可能會被編譯器當(dāng)成函數(shù)處理)
3.內(nèi)聯(lián)函數(shù)不建議聲明和定義分離,會產(chǎn)生鏈接錯誤(鏈接階段,符號表里沒有函數(shù)地址,鏈接找不到)。內(nèi)聯(lián)函數(shù)在調(diào)用的地方展開,不進(jìn)入符號表,函數(shù)的地址是個跳轉(zhuǎn)指令。
八、auto關(guān)鍵字(c++11)
程序越來越復(fù)雜,用到的類型也越來越復(fù)雜:
1.類型難于拼寫。
2.含義不明確導(dǎo)致容易出錯。
auto的實(shí)際價值就是簡化代碼,類型很長時,可以直接推導(dǎo),用它比較方便。
案例:
使用typedef給類型取別名,可以簡化代碼,但是也會遇到新的問題
在編譯的時候,常常需要把表達(dá)式的值賦給變量,函數(shù)聲明變量的時候,要清除的知道表達(dá)式的類型,要做到這點(diǎn)非常不容易,所以c++給auto賦予了新的含義。
8.1 auto的使用
早期C/C++中auto的含義是:使用auto修飾的變量,是具有自動存儲器的局部變量。
C++11中,標(biāo)準(zhǔn)委員會賦予了auto全新的含義即: 作為一個新的類型指示符來指示編譯器,auto聲明的變量必須由編譯器在編譯時期推導(dǎo)而得。
auto的使用:
#include <iostream>
using namespace std;
int main()
{
int a = 2;
auto p = a;//p是整型
auto p1 = &a;//p1是指針類型
auto* p2 = &a;//顯示的指令必須是個指針
auto& p3 = a;//顯示的指令必須是引用
//查看它們的類型
cout << typeid(p).name() << endl;
cout << typeid(p1).name() << endl;
cout << typeid(p2).name() << endl;
cout << typeid(p3).name() << endl;
return 0;
}
注意:
使用auto定義變量時必須對其進(jìn)行初始化,在編譯階段編譯器需要根據(jù)初始化表達(dá)式來推導(dǎo)auto的實(shí)際類型。因此auto并非是一 種“類型”的聲明,而是一個類型聲明時的“占位符”, 編譯器在編譯期會將auto替換為變量實(shí)際的類型。
1.auto與指針和引用結(jié)合起來使用
用auto聲明指針類型時,用auto和auto*沒有區(qū)別,但是用auto聲明引用類型時,必須加&
2.在同一行定義多個變量
在同一行聲明多個變量,必須是相同類型,否則編譯器會報(bào)錯,因?yàn)榫幾g器實(shí)際上只對第一個類型進(jìn)行推導(dǎo),然后用推導(dǎo)來的類型定義其它的變量。
8.2 auto不能用的場景
1.auto不能做函數(shù)的參數(shù)
auto不能作為形參類型,因?yàn)榫幾g器無法對形參的實(shí)際參數(shù)進(jìn)行推導(dǎo)
2.auto不能直接用來聲明數(shù)組。
3.為了避免與c++98中的auto混淆,c++11只保留了auto作為類型指示符的用法。
4.auto常用的優(yōu)勢用法是與c++11提供的新式for循環(huán),還有l(wèi)ambda表達(dá)式等進(jìn)行配合使用。
九、基于范圍的for循環(huán)(c++11)
c++11引入了基于范圍的for循環(huán)。for循環(huán)后面的括號被冒號‘:’分成了兩部分,左邊部分是在范圍內(nèi)用于迭代的變量,右邊部分是被迭代的范圍。
#include <iostream>
using namespace std;
int main()
{
//定義一個數(shù)組
int arr[] = { 1,2,3,4,5,6 };
//讓數(shù)組的值加2
for (int& a : arr)
a = a + 2;
//遍歷數(shù)組
for (int a : arr)
cout << a << " ";
cout << endl;
return 0;
}
注意:與普通循環(huán)類似,可以用continue來結(jié)束本次循環(huán),也可以用break跳出整個循環(huán)。
9.1 范圍for的使用條件
1.for循環(huán)迭代的范圍必須明確
對于數(shù)組而言就是數(shù)組中的第一個元素和最后一個元素的范圍;對于類而言,應(yīng)該提供begin和end的方法,begin和end就是for循環(huán)迭代的范圍。
2 .迭代的操作要實(shí)現(xiàn)++和==的操作。(還沒學(xué)到這個知識點(diǎn))
十、指針空值——nullptr(c++11)
良好的c++編程習(xí)慣中,聲明一個變量最好給它一個初始值,否則容易出現(xiàn)不可預(yù)料的錯誤。
我們一般會給指針進(jìn)行初始化:
int* p1= NULL;
int* p2=0;
NULL實(shí)際是一個宏,在c頭文件(stddef.h)中:
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
可以看到,NULL被定義成了字面常量0,或者被定義成了無類型指針(void*)的常量。
#include <iostream>
using namespace std;
void f(int)
{
cout << "f(int)" << endl;
}
void f(int*)
{
cout << "f(int*)" << endl;
}
int main()
{
f(0);
f(NULL);
f((int*)NULL);
f((int*)0);
return 0;
}
解釋:文章來源:http://www.zghlxwxcb.cn/news/detail-714206.html
程序本意是想通過f(NULL)調(diào)用指針版本的(int*)函數(shù),但是由于NULL被定義成0,因此與程序的初衷相悖。
在C++98中,字面常量0既可以是一個整形數(shù)字, 也可以是無類型的指針(void*)常量,但是編譯器默認(rèn)情況下將其看成是一個整形常量, 如果要將其按照指針方式來使用,必須對其進(jìn)行強(qiáng)轉(zhuǎn)(void*)0。
注意:
1.在使nullptr表示指針空值時,不需要包含頭文件,因?yàn)閚ullptr是c++1作為新關(guān)鍵字引入的。
2.在c++中,sizeof(nullptr)和sizeof((void *)0)所占的字節(jié)數(shù)相同。
3.為了提高代碼的實(shí)用性,后續(xù)表示指針空值時,最好使用nullptr。文章來源地址http://www.zghlxwxcb.cn/news/detail-714206.html
到了這里,關(guān)于小白學(xué)習(xí)c++的的第一節(jié)課的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!