柚子快報(bào)邀請(qǐng)碼778899分享:【C++】初識(shí)C++
柚子快報(bào)邀請(qǐng)碼778899分享:【C++】初識(shí)C++
【C++】初識(shí)C++
文章概括關(guān)鍵字(C++98)命名空間命名空間的定義命名空間的特性
輸入與輸出C++中的輸入輸出輸入輸出的命名空間
缺省參數(shù)函數(shù)重載引用引用的概念引用的特性引用地使用場(chǎng)景引用做參數(shù)引用做返回值
常引用常引用的倆個(gè)例子
引用與指針的區(qū)別
內(nèi)聯(lián)函數(shù)
文章概括
談?wù)凜++在學(xué)習(xí)前的認(rèn)知,C++是在C的基礎(chǔ)上,容納進(jìn)去了面向?qū)ο蟮木幊趟枷?,并增加了許多有用的庫(kù),以及編程范式等等。所以學(xué)習(xí)C++之前一定對(duì)C有一定的認(rèn)知,一個(gè)好的C++程序員一定會(huì)是一個(gè)優(yōu)秀的C語(yǔ)言程序員。
本章主要介紹:補(bǔ)充C語(yǔ)言語(yǔ)法的不足,以及C++是如何對(duì)C語(yǔ)言程序設(shè)計(jì)不合理地方進(jìn)行優(yōu)化的,比如:作用域方面、IO方面、函數(shù)方面、指針?lè)矫妗⒑攴矫娴鹊?;同時(shí)也為后續(xù)學(xué)習(xí)類和對(duì)象做了鋪墊。
關(guān)鍵字(C++98)
C++有63個(gè)關(guān)鍵字,C語(yǔ)言有32個(gè)關(guān)鍵字。
關(guān)鍵字關(guān)鍵字關(guān)鍵字關(guān)鍵字關(guān)鍵字asmautoboolbreakcasecatchcharclassconstconst_castdeletedodoubledynamic_castelseenumexplicitexportexternfalsefloatgotoifinlineintlongmutablenamespacenewoperatorprivateprotectedreinterpret_castreturnshortsignedsizeofstaticstatic_casestructswitchtemplatethistrytypedeftypeidtypenameunionunsignedusingvirtualvoidvolatilecontinueforpublicthrowwchar_tdefaultfriendregistertruewhile
后續(xù)逐漸了解
命名空間
命名空間的定義
先以C語(yǔ)言舉例: 假設(shè)需要定義一個(gè)全局變量隨機(jī)數(shù)random為10
#include
int rand = 10;
int main(void)
{
printf("%d\n", rand);
return 0;
}
這是可以編譯成功的,但是我們之前有了解過(guò)rand是一個(gè)頭文件stdlib.h的一個(gè)庫(kù)函數(shù),如果我們包含stdlib.h這個(gè)頭文件會(huì)發(fā)生什么?
#include
#include
int rand = 10;
int main(void)
{
printf("%d\n", rand);
return 0;
}
發(fā)生報(bào)錯(cuò),這里可以明顯突出一個(gè)C語(yǔ)言的庫(kù)命名沖突問(wèn)題。
有時(shí)在一個(gè)大的工程中有多個(gè)項(xiàng)目,每個(gè)項(xiàng)目會(huì)由不同的人負(fù)責(zé),這時(shí)也會(huì)難免遇到項(xiàng)目之間的命名問(wèn)題。
總之,C語(yǔ)言命名沖突的問(wèn)題有: 1.庫(kù)命名沖突問(wèn)題 2.項(xiàng)目相互之間命名的沖突
在C++中,存在命名空間namespace可以解決這類型的問(wèn)題。
在講解命名空間前,需要先了解域的概念:域可以看作是一個(gè)作用區(qū)域,域包含類域、命名空間域、局部域、全局域等等
在一般情況下訪問(wèn)時(shí),會(huì)先訪問(wèn)局部域,在局部域中未發(fā)現(xiàn)變量,會(huì)進(jìn)而訪問(wèn)全局域。
假設(shè)在全局域中存在全局變量,同時(shí)在局部域中也存在一個(gè)局部變量,但是想要跳過(guò)局部域直接訪問(wèn)全局域,應(yīng)該如何操作?
int a = 1;
int main(void)
{
int a = 0;
printf("%d\n", ::a);
return 0;
}
這里需要介紹一個(gè)操作符"::",域操作限定符,::a默認(rèn)會(huì)跳過(guò)局部域,訪問(wèn)全局域。
那如果存在命名空間域namespace,其優(yōu)先級(jí)是如何?
int a = 1;
namespace project
{
int a = 2;
}
int main(void)
{
int a = 0;
printf("%d\n", a);
printf("%d\n", ::a);
return 0;
}
由此可見(jiàn),訪問(wèn)變量a,會(huì)先訪問(wèn)局部域——>然后訪問(wèn)全局域——>最后在默認(rèn)情況下,編譯器并不會(huì)主動(dòng)去命名空間域搜索。
想要搜索命名空間域,有倆種方式: 1.展開(kāi)命名空間域
namespace project
{
int a = 2;
}
using namespace project;
int main(void)
{
printf("%d\n", a);
return 0;
}
2.指定訪問(wèn)命名空間域
namespace project
{
int a = 2;
}
int main(void)
{
printf("%d\n", project::a);
return 0;
}
如果在局部域,全局域,命名空間域(展開(kāi)命名空間域)中都存在變量a,會(huì)如何訪問(wèn)?
這里可以發(fā)現(xiàn)局部域的優(yōu)先級(jí)最高,但如果只存在全局域與展開(kāi)后命名空間域時(shí),會(huì)發(fā)生報(bào)錯(cuò),原因在于:展開(kāi)的命名空間域相當(dāng)于暴露在全局域。
所以不要輕易使用using namespace + 名,即不要請(qǐng)輕易展開(kāi)命名空間域。
總結(jié):在C/C++中,變量、函數(shù)和后面要學(xué)到的類都是大量存在的,這些變量、函數(shù)和類的名稱將都存在于全局作用域中,可能會(huì)導(dǎo)致很多沖突。使用命名空間的目的是對(duì)標(biāo)識(shí)符的名稱進(jìn)行本地化,以避免命名沖突或名字污染,namespace關(guān)鍵字的出現(xiàn)就是針對(duì)這種問(wèn)題的。
命名空間的特性
定義命名空間,需要使用到namespace關(guān)鍵字,后面跟命名空間的名字,然后接一對(duì){}即可,{}中即為命名空間的成員。
namespace project
{
int a = 2;
}
1.命名空間域中可以定義變量、函數(shù)、類型
namespace project
{
//定義變量
int num = 10;
//定義函數(shù)
int add(int x, int y)
{
return x + y;
}
//定義結(jié)構(gòu)體
struct Node
{
struct Node* next;
int data;
};
}
2.命名空間可以嵌套
namespace project
{
namespace N1
{
int a = 1;
}
namespace N2
{
int a = 2;
//定義函數(shù)
int add(int x, int y)
{
return x + y;
}
}
}
int main(void)
{
printf("%d ", project::N1::a);
printf("%d ", project::N2::a);
printf("%d ", project::N2::add(1,2));
return 0;
}
std是C++標(biāo)準(zhǔn)庫(kù)的命名空間名,C++將標(biāo)準(zhǔn)庫(kù)的定義實(shí)現(xiàn)都放在這個(gè)命名空間里。
【注意】:一個(gè)命名空間就定義了一個(gè)新的作用域,命名空間中的所有內(nèi)容都局限于該命名空間中
3.同一個(gè)工程中允許存在多個(gè)相同的命名空間,編譯器最后會(huì)合成同一個(gè)命名空間,即可以在多個(gè)文件中定義相同名字的命名空間
輸入與輸出
C++中的輸入輸出
早年在VC6.0時(shí)沒(méi)有命名空間,頭文件C++中的頭文件
#include
后面改為了
#include
#include
#include
使用iostream這個(gè)頭文件時(shí),需要先學(xué)習(xí)C++的輸入輸出. c語(yǔ)言中使用printf與scanf來(lái)將數(shù)據(jù)輸出與輸入,而在C++中使用cout與cin實(shí)現(xiàn)輸入輸出。
#include
using namespace std;
int main(void)
{
int a;
cin >> a;
cout << a << endl;
return 0;
}
說(shuō)明: 1.使用cout標(biāo)準(zhǔn)輸出對(duì)象(控制臺(tái))和cin標(biāo)準(zhǔn)輸入對(duì)象(鍵盤(pán))時(shí),必須包含頭文件iostream,以及按照命名空間使用方法使用std。 2.cout和cin是全局的流對(duì)象,endl是特殊的C++符號(hào),表示換行輸出,它們都包含在iostream頭文件中 3.<<是流插入運(yùn)算符,>>是流提取運(yùn)算符 4.cout和cin的使用比較方便,不需要同printf與scanf一樣手動(dòng)控制格式,C++的輸入輸出可以手動(dòng)控制變量類型
輸入輸出的命名空間
使用輸入輸出有3種情況: 1.指定訪問(wèn)命名空間域
#include
int main(void)
{
std::cout << "Hello World!" << std::endl;
return 0;
}
2.使用展開(kāi)命名空間域
#include
using namespace std;
int main(void)
{
cout << "Hello World!" << endl;
return 0;
}
編譯器會(huì)去std這個(gè)命名空間搜索(std這個(gè)命名空間域中封有iostream) 【注意】直接展開(kāi)std會(huì)有很大的風(fēng)險(xiǎn),當(dāng)存在自己定義的名字與庫(kù)中名字重合會(huì)報(bào)錯(cuò),建議項(xiàng)目中不要展開(kāi),日常使用可以進(jìn)行展開(kāi),項(xiàng)目中建議指定訪問(wèn),不要輕易展開(kāi)命名空間。
3.展開(kāi)部分命名
#include
using std::cout;
using std::endl;
int main(void)
{
cout << "Hello World!" << endl;
return 0;
}
缺省參數(shù)
缺省參數(shù)也稱默認(rèn)參數(shù),即函數(shù)在傳參的時(shí)候可以存在缺省參數(shù)(默認(rèn)參數(shù))。
void Init(int* node, int sz = 4)
{
int* newnode = (int*)malloc(sizeof(int) * sz);
if (newnode == NULL)
{
perror("malloc fail");
return;
}
node = newnode;
}
int main(void)
{
int* node;
//默認(rèn)情況下,初始化4個(gè)字節(jié)
Init(node);
//可以指定實(shí)參,初始化100個(gè)字節(jié)
Init(node, 100);
return 0;
}
觀察代碼,在C++中傳參存在倆種情況: 1.沒(méi)有參數(shù)時(shí),使用參數(shù)的默認(rèn)值 2.有任何參數(shù)時(shí),使用指定的實(shí)參 即實(shí)參的優(yōu)先級(jí)最大,當(dāng)不存在實(shí)參時(shí),使用默認(rèn)參數(shù) 【注意】當(dāng)存在多個(gè)缺省參數(shù)時(shí),不允許跳著傳參,只能從左到右順序傳參
#include
using namespace std;
//全缺省
int RetAdd(int a = 1, int b = 2, int c = 3)
{
return a + b + c;
}
int main(void)
{
int sum = RetAdd();
cout << sum << endl;
return 0;
}
#include
using namespace std;
//半缺省
int RetAdd(int a, int b, int c = 3)
{
return a + b + c;
}
int main(void)
{
int sum = RetAdd(1,2);
cout << sum << endl;
return 0;
}
C++中全缺省與半缺省的概念 全缺?。核械膮?shù)都給了缺省值 半缺?。喝笔〔糠謪?shù) 【注意】半缺省參數(shù)必須從右至左依次缺省,切勿間隔缺省
【注意】在使用缺省參數(shù)使用需要注意,聲明與定義不能同時(shí)給缺省值,一般在聲明時(shí)存在缺省值,定義時(shí)不存在缺省值。 如果了解文件的編譯與鏈接可以知道,編譯期間只能看到聲明,鏈接期間可以看到定義
函數(shù)重載
重載的意思是:一詞多義 那么函數(shù)重載:是函數(shù)的一種特殊情況,C++允許在同以作用域中聲明幾個(gè)功能類似的同名函數(shù),這些函數(shù)的形參列表(參數(shù)個(gè)數(shù)、參數(shù)類型、類型順序)不同,常用來(lái)處理實(shí)現(xiàn)功能類似數(shù)據(jù)類型不同的問(wèn)題。
參數(shù)類型不同
#include
using namespace std;
//參數(shù)類型不同
int RetAdd(int a, int b)
{
return a + b;
}
double RetAdd(double a, double b)
{
return a + b;
}
int main(void)
{
cout << RetAdd(1, 2) << endl;
cout << RetAdd(1.5, 2.2) << endl;
return 0;
}
參數(shù)個(gè)數(shù)不同
#include
using namespace std;
//參數(shù)個(gè)數(shù)不同
void Fun()
{
cout << "無(wú)參數(shù)" << endl;
}
void Fun(int a)
{
cout << "有參數(shù)" << endl;
}
int main(void)
{
Fun();
Fun(1);
return 0;
}
類型順序不同
#include
using namespace std;
//類型順序不同
double RetAdd(int a, double b)
{
return a + b;
}
double RetAdd(double a, int b)
{
return a + b;
}
int main(void)
{
cout << RetAdd(1, 2.3) << endl;
cout << RetAdd(1.9, 2) << endl;
return 0;
}
【注意】有三個(gè)不構(gòu)成函數(shù)重載的例子 1.僅返回值不同
2.僅變量名不同
3.不明確的函數(shù)調(diào)用
引用
引用的概念
引用不是新定義一個(gè)變量,而是給已存在變量取一個(gè)別名,編譯器不會(huì)為引起變量開(kāi)辟內(nèi)存空間,它和它的變量共用同一塊內(nèi)存空間。
#include
using namespace std;
int main(void)
{
int a = 10;
int& b = a;
int& c = b;
int& d = a;
printf("%p\n", &a);
printf("%p\n", &b);
printf("%p\n", &c);
printf("%p\n", &d);
return 0;
}
類型& : 引用變量名(對(duì)象名) = 引用實(shí)體
在學(xué)習(xí)C語(yǔ)言階段,想要找到一個(gè)變量,可以使用指針,而在C++中引入了引用的概念,可以大幅度代替指針的作用。
int main(void)
{
int a = 0;
int* pa = &a;
int** ppa = &pa;
printf("%p\n", &a);
printf("%p\n", pa);
printf("%p\n", *ppa);
return 0;
}
引用的特性
1.引用在定義時(shí)必須需要初識(shí)化在這里插入圖片描述 2.一個(gè)變量可以有多個(gè)引體
#include
using namespace std;
int main(void)
{
int a = 10;
int& b = a;
int& c = a;
int& d = a;
return 0;
}
3.引用一旦引用一個(gè)實(shí)體,便不可再引用其他引體
#include
using namespace std;
int main(void)
{
int a = 10;
int x = 20;
int& b = a;
b = x;
cout << b << endl;
return 0;
}
引用地使用場(chǎng)景
引用做參數(shù)
引用做參數(shù)時(shí),可以作為輸出型參數(shù)使用,何為輸出型參數(shù)?
函數(shù)在傳參地時(shí)候,會(huì)傳輸出型參數(shù)或者輸入型參數(shù),輸入型參數(shù)的意思是形參的改變不可以改變實(shí)參,即形參是實(shí)參的一份臨時(shí)拷貝;輸出型參數(shù)的意思是形參的改變要改變實(shí)參。
在C語(yǔ)言中一般使用指針來(lái)做輸出型參數(shù):
//鏈表
typedef struct ListNode
{
int data;
struct ListNode* next;
}PNode;
void LTPushBack(PNode* p, int x);
而在C++中,可以使用引用來(lái)做輸出型參數(shù):
//鏈表
typedef struct ListNode
{
int data;
ListNode* next;
//在C++中可以不寫(xiě)struct
}*PNode;
void LTPushBack(PNode& p, int x);
這段代碼的意思是:定義一個(gè)結(jié)構(gòu)體的指針,引用這個(gè)指針并使用phead作為別名。
引用做參數(shù),同時(shí)也可以提高效率,但是只存在于數(shù)量較大對(duì)象或者深拷貝類對(duì)象。
下面這段代碼可以比較傳值與傳引用的效率對(duì)比:
#include
struct A { int a[100000]; };
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();
// 分別計(jì)算兩個(gè)函數(shù)運(yùn)行結(jié)束后的時(shí)間
cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}
由于函數(shù)在傳參期間,以值作為參數(shù),函數(shù)不會(huì)之間傳遞實(shí)參或者將變量本身直接返回,而是傳遞實(shí)參的一份臨時(shí)拷貝,因此以值作為參數(shù)時(shí)的效率是很低的,尤其是參數(shù)非常大時(shí),效率會(huì)更低。
對(duì)象越大,代價(jià)就會(huì)越大,提高的效率就越多
【注意】引用相比較于指針沒(méi)有質(zhì)的區(qū)別,但是在C++中實(shí)際情況下引用的使用情況較多。
引用做返回值
int Count()
{
static int n = 0;
n++;
return n;
}
int main(void)
{
int ret = Count();
return 0;
}
觀察這段代碼,使用傳值返回時(shí),會(huì)生成臨時(shí)變量,可能會(huì)存放在寄存器中(寄存器的大小為4/8個(gè)字節(jié),如果寄存器內(nèi)存不夠,會(huì)向上申請(qǐng)),作為int ret = Count();這段代碼的返回值。
雖然n被static int修飾,成為靜態(tài)變量,存放在靜態(tài)區(qū)中,但是不管有沒(méi)有static修飾或者存在全局變量,函數(shù)都會(huì)根據(jù)返回值int,創(chuàng)造出一個(gè)臨時(shí)變量,這樣會(huì)大大降低效率,那么使用引用做返回值可以解決這樣的問(wèn)題。
下面就是引用做返回值的第一個(gè)作用:
減少拷貝,提高效率
int& Count()
{
static int n = 0;
n++;
return n;
}
int main(void)
{
int ret = Count();
return 0;
}
下面這段代碼可以比較傳值返回與傳引用返回:
#include
struct 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();
// 計(jì)算兩個(gè)函數(shù)運(yùn)算完成之后的時(shí)間
cout << "TestFunc1 time:" << end1 - begin1 << endl;
cout << "TestFunc2 time:" << end2 - begin2 << endl;
}
由于函數(shù)在傳值返回期間,以值作為返回值,函數(shù)不會(huì)之間傳遞實(shí)參或者將變量本身直接返回,而是返回變量的一份臨時(shí)拷貝,因此以值作為返回值時(shí)的效率是很低的,尤其是參數(shù)非常大時(shí),效率會(huì)更低。
使用引用做返回值,還有另外一個(gè)作用:
修改返回值以及獲取返回值
typedef struct SeqList
{
int arr[100];
int size;
}SL;
int& SLPostion(SL& s, int pos)
{
assert(pos < 100 && pos >= 0);
return s.arr[pos];
}
int main(void)
{
SL s;
SLPostion(s, 0) = 1;
int ret = SLPostion(s, 0);
cout << ret << endl;
cout << SLPostion(s, 0) << endl;
return 0;
}
如果不使用static修飾靜態(tài)變量,使用引用做返回值時(shí),結(jié)果時(shí)不確定的。
int& Count()
{
int n = 0;
n++;
return n;
}
int main(void)
{
int& ret = Count();
cout << ret << endl;
return 0;
}
這段代碼雖然不會(huì)報(bào)錯(cuò),但是明顯可以發(fā)現(xiàn),ret訪問(wèn)是存在越界訪問(wèn)。
int& Count(int n)
{
n++;
return n;
}
int main(void)
{
int& ret = Count(10);
cout << ret << endl;
Count(20);
cout << ret << endl;
return 0;
}
觀察這段代碼,如果count函數(shù)結(jié)束,函數(shù)建立的棧幀會(huì)銷(xiāo)毀,在vs編譯器上沒(méi)有清理?xiàng)瑀et的值是第一次函數(shù)調(diào)用結(jié)束后,第二次函數(shù)建立在同樣的位置。
int& Count(int n)
{
n++;
return n;
}
int main(void)
{
int& ret = Count(10);
cout << ret << endl;
rand();
cout << ret << endl;
return 0;
}
若是隨意插入一個(gè)函數(shù),則ret的值變成了隨機(jī)值。
可以發(fā)現(xiàn),在這種情況下使用引用是很危險(xiǎn)的。
總結(jié): 1.基本任何場(chǎng)景都是可以使用引用傳參 2.謹(jǐn)慎用引用做返回值,出了函數(shù)作用域,對(duì)象不在了就不能使用引用返回,如果對(duì)象還在就可以使用引用返回。 2.可以使用引用返回的場(chǎng)景:靜態(tài)變量、全局變量、堆區(qū)malloc或者calloc
常引用
const int a = 0;
int& b = a;
觀察這段代碼,當(dāng)變量a被const修飾變成不可修改的左值時(shí),使用int引用是不可以的,原因是在引用的過(guò)程中國(guó)權(quán)限不能放大。
int a = 0;
int& b = a;
const int a = 0;
const int& b = a;
這倆種情況是被允許的,這倆種情況是權(quán)限的平移。
int a = 0;
const int& b = a;
這種情況也是被允許的,這種情況被稱為權(quán)限的縮小。
int a = 0;
const int& b = a;
a++;
這種情況也是被允許的,原因是編譯器僅縮小了a和b所在地址的引用b的權(quán)限,而并沒(méi)有縮小a的權(quán)限所有a++是被允許的,而b++是不被允許的。
const int& x = 10;
同時(shí),給變量取別名也是被允許的。
常引用的倆個(gè)例子
double d = 1.11;
int i = d;
我們都知道double在轉(zhuǎn)變?yōu)閕nt時(shí),會(huì)進(jìn)行類型轉(zhuǎn)換,由于double是8個(gè)字節(jié),int是4個(gè)字節(jié),double變成int會(huì)進(jìn)行截?cái)?,而截?cái)嗟倪^(guò)程會(huì)建立一個(gè)新的臨時(shí)變量,臨時(shí)變量具有常性,即臨時(shí)變量是不可修改的值。
double d = 1.11;
int& i = d;
所有這段代碼是錯(cuò)誤的,這里double變成臨時(shí)變量權(quán)限縮小,而int&會(huì)將權(quán)限放大,引用的過(guò)程中權(quán)限是不可以放大的。
double d = 1.11;
const int& i = d;
這段代碼是正確的,這里進(jìn)行了權(quán)限的平移。
int Fun()
{
static int x = 0;
return x;
}
int main(void)
{
int& ret1 = Fun();
const int& ret2 = Fun();
}
第二個(gè)例子是關(guān)于函數(shù)在釋放前會(huì)建立一個(gè)臨時(shí)變量給返回值提供位置。此時(shí)這個(gè)臨時(shí)變量也是具有常性,是不可以修改的,即ret1是錯(cuò)誤的代碼,而ret2是正確的代碼。
引用與指針的區(qū)別
引用與指針在語(yǔ)法層面是不同的,引用不開(kāi)空間,引用是對(duì)變量取別名,而指針不同,指針開(kāi)空間,指針是存儲(chǔ)變量的地址。
int x = 10;
int* y = &x;
int a = 20;
int& b = a;
觀察這段代碼,可以發(fā)現(xiàn)從底層匯編指令實(shí)現(xiàn)的角度來(lái)看,引用是類似指針的方式實(shí)現(xiàn)的。
指針與引用的不同點(diǎn): 1.引用概念上定義一個(gè)變量的別名,而指針存儲(chǔ)一個(gè)變量的地址 2.引用在定義時(shí)必須初始化,而指針沒(méi)有要求 3.引用在初始化時(shí)引用一個(gè)實(shí)體后,就不可以引用其他實(shí)體,而指針可以在任何時(shí)候指向任何一個(gè)同類型的實(shí)體 4.沒(méi)有NULL引用,但有NULL指針 5.引用和指針在sizeof中含義不同,引用結(jié)果為引用類型的大小,而指針始終是地址空間所占字節(jié)個(gè)數(shù)(32位平臺(tái)下占4個(gè)字節(jié)) 6.引用自加即引用的實(shí)體增加1,指針自加即指針向后偏移一個(gè)指針類型的位置 7.有多級(jí)指針,但沒(méi)有多級(jí)引用 8.訪問(wèn)實(shí)體的方式不同,指針需要解引用,引用編譯器會(huì)自己處理 9.引用比指針使用起來(lái)相對(duì)安全
內(nèi)聯(lián)函數(shù)
在了解內(nèi)聯(lián)函數(shù)之前,應(yīng)該對(duì)C語(yǔ)言中的宏的定義有一定了解。
//宏定義
#define ADD(x,y) ((x) + (y)) * 10
//注意規(guī)范,宏定義是完整的替換
int main(void)
{
for (int i = 0; i < 100; i++)
{
cout << ADD(i, i + 1) << endl;
}
return 0;
}
在C語(yǔ)言中,使用宏定義來(lái)解決函數(shù)建立過(guò)多且函數(shù)內(nèi)容較少的問(wèn)題。但是宏在使用過(guò)程中也存在著也許優(yōu)點(diǎn)與缺點(diǎn): 宏函數(shù)的優(yōu)點(diǎn):不需要建立棧幀,提高調(diào)用效率,增強(qiáng)代碼的復(fù)用性 宏函數(shù)的缺點(diǎn):復(fù)雜、容易出錯(cuò);可讀性差;不能調(diào)試。
在C++中,會(huì)使用內(nèi)聯(lián)函數(shù)來(lái)代替部分宏函數(shù)。 以inline修飾的函數(shù)被稱為內(nèi)聯(lián)函數(shù),編譯時(shí)C++編譯器會(huì)根據(jù)情況在調(diào)用內(nèi)聯(lián)函數(shù)的地方進(jìn)行展開(kāi),沒(méi)有函數(shù)調(diào)用建立棧幀的開(kāi)銷(xiāo),內(nèi)聯(lián)函數(shù)提高程序運(yùn)行的速度。
//內(nèi)聯(lián)函數(shù)
inline int Add(int x, int y)
{
return (x + y) * 10;
}
int main(void)
{
for (int i = 0; i < 100; i++)
{
cout << Add(i, i + 1) << endl;
}
return 0;
}
由此可知,宏函數(shù)與內(nèi)聯(lián)函數(shù)是很相似的,但C++對(duì)宏函數(shù)進(jìn)行了優(yōu)化,宏函數(shù)與內(nèi)聯(lián)函數(shù)適用于短小,需要頻繁調(diào)用的函數(shù)。
但是并不是所有的函數(shù)都可以使用內(nèi)聯(lián)函數(shù),否則就會(huì)導(dǎo)致可執(zhí)行程序變大。 這里假設(shè)Func不是內(nèi)聯(lián)函數(shù)(不被inline修飾),每次執(zhí)行Func函數(shù)時(shí)都會(huì)跳轉(zhuǎn)到Func去執(zhí)行,如果存在n個(gè)位置調(diào)用Func函數(shù),則合計(jì)會(huì)有m+n條指令;
這里假設(shè)Func是內(nèi)聯(lián)函數(shù)(被inline修飾),相當(dāng)于每次執(zhí)行Func函數(shù)都會(huì)對(duì)Func進(jìn)行展開(kāi),如果存在n個(gè)位置調(diào)用Func函數(shù),則合計(jì)會(huì)有m*n條指令。
若調(diào)用Func函數(shù)過(guò)多,則會(huì)導(dǎo)致可執(zhí)行程序變大。
編譯器在識(shí)別被inline修飾的內(nèi)聯(lián)函數(shù)時(shí),內(nèi)聯(lián)函數(shù)對(duì)編譯器只是一個(gè)建議,最終時(shí)候成為內(nèi)聯(lián)函數(shù),編譯器會(huì)自己決定,在這些情況下,編譯器會(huì)自動(dòng)否決內(nèi)聯(lián):1.比較長(zhǎng)的函數(shù);2.遞歸函數(shù)。
默認(rèn)在debug版本下,inline不會(huì)起作用,否則無(wú)法支持調(diào)試。在debug版本下,需要對(duì)編譯器進(jìn)行設(shè)置:
這里可以發(fā)現(xiàn),在調(diào)用代碼較少時(shí),此時(shí)內(nèi)聯(lián)函數(shù)起效果;
inline int Add(int x, int y)
{
for (int i = 0; i < 100; i++)
{
x *= 2;
}
for (int i = 0; i < 100; i++)
{
x /= 2;
}
for (int i = 0; i < 100; i++)
{
y *= 2;
}
for (int i = 0; i < 100; i++)
{
y /= 2;
}
return x + y;
}
int main(void)
{
int ret = Add(1, 2);
return 0;
}
此時(shí),函數(shù)內(nèi)容過(guò)多,內(nèi)聯(lián)函數(shù)被編譯器否決;
inline int Add(int x,int y)
{
if (x > 5 && y > 5)
return x + y;
return Add(x + 1, y + 1);
}
int main(void)
{
int ret = Add(1, 2);
cout << ret << endl;
return 0;
}
此時(shí),函數(shù)遞歸,內(nèi)聯(lián)函數(shù)被編譯器否決;
//Func.h文件
inline int Add(int x, int y);
//Func.cpp文件
#include"Func.h"
inline int Add(int x, int y)
{
return (x + y) * 10;
}
//test.cpp文件
#include
#include"Func.h"
using namespace std;
int main(void)
{
int ret = Add(1, 2);
cout << ret << endl;
return 0;
}
如果內(nèi)聯(lián)函數(shù)沒(méi)有被編譯器否決,那么內(nèi)聯(lián)函數(shù)就會(huì)在編譯期間會(huì)被展開(kāi),建議內(nèi)聯(lián)函數(shù)聲明與定義不分離,直接寫(xiě)在頭文件中。 原因是,內(nèi)聯(lián)函數(shù)不會(huì)被call,所有內(nèi)聯(lián)函數(shù)就不會(huì)進(jìn)入符號(hào)表進(jìn)行鏈接,如果聲明與定義分離,會(huì)導(dǎo)致鏈接錯(cuò)誤,內(nèi)聯(lián)函數(shù)被展開(kāi)后尋找不到函數(shù)地址,鏈接就會(huì)找不到。
柚子快報(bào)邀請(qǐng)碼778899分享:【C++】初識(shí)C++
參考鏈接
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。