}
int main()
{
//參數(shù)順序不同
Print(5,'a');
Print('a', 5);
return 0;
}
注意: 如果兩個(gè)函數(shù)函數(shù)名和參數(shù)是一樣的,返回值不同是不構(gòu)成重載的,因?yàn)檎{(diào)用時(shí)編譯器無(wú)法區(qū)分。
我們知道C語(yǔ)言不支持函數(shù)重載,在鏈接時(shí),直接用函數(shù)名去尋找地址,當(dāng)有同名函數(shù)的時(shí)候,是區(qū)分不開(kāi)的。
C++是采用了名字修飾規(guī)則,函數(shù)名字引入?yún)?shù)類(lèi)型(在不同平臺(tái)上的名字修飾規(guī)則是不同的)
在linux環(huán)境下,采用gcc編譯完成后,函數(shù)名字的修飾沒(méi)有發(fā)生改變 (編譯C語(yǔ)言)
采用C++編譯器編譯后結(jié)果 ( 編譯C++ )
g++ -o testcpp test.cc
objdump -S testcpp
我們可以看出gcc的函數(shù)修飾后名字不變。而g++的函數(shù)修飾后變成【_Z+函數(shù)長(zhǎng)度+函數(shù)名+類(lèi)型首字母】
在vs下的命名規(guī)則
windows下vs編譯器對(duì)函數(shù)名字修飾規(guī)則相對(duì)復(fù)雜難懂。
五、 引用
1. 引用的概念
引用(取別名):引用是給已經(jīng)存在的變量取了一個(gè)別名,不是新定義一個(gè)變量,編譯器不會(huì)為引用變量開(kāi)辟內(nèi)存空間,和所引用的變量共用一塊內(nèi)存空間。
例如:張三,小名叫:小張 。張三和小張都是只一個(gè)人。
類(lèi)型& 引用變量名 = 引用實(shí)體;//這里引用變量名就是該變量的別名
例:
//引用
#include
using namespace std;
int main()
{
int a = 10;
int& b = a; //引用,b是a的別名
cout << b << endl;
return 0;
}
輸出結(jié)果: 10
我們通過(guò)打印a,b這兩個(gè)地址進(jìn)行打印,查看地址是否一致
注意:引用類(lèi)型必須和引用實(shí)體是同種類(lèi)型的
2. 引用的特性
引用在定義時(shí)必須初始化一個(gè)變量可以有多個(gè)引用引用一旦引用一個(gè)實(shí)體,再不能引用其他實(shí)體
引用在定義時(shí)必須進(jìn)行初始化 int& b; //這種寫(xiě)法是錯(cuò)誤的
int& b = a; //引用在定義時(shí)必須初始化
一個(gè)變量可以有多個(gè)引用(就像一個(gè)人的外號(hào)可以有多個(gè),但是都是指的是同一個(gè)人) int a = 10;
int& b = a; //引用,b是a的別名
int& c = a; //引用,c也是a的別名
引用一旦引用一個(gè)實(shí)體,再不能引用其他實(shí)體 也就是當(dāng)引用一個(gè)變量時(shí),該引用不能再去引用其他變量 int a = 10;
int x = 20;
int& b = a; //引用,b是a的別名
int& b = x; //error ,這樣會(huì)導(dǎo)致重定義,多次初始化
關(guān)于常量引用
//int& z = 10; //error 常量不能引用
const int a = 100;
//int& b = a; //error,編譯時(shí)出錯(cuò),a為常量
const int& b = a; //正確寫(xiě)法
int ra = 10;
//double& b = ra; //error, 引用類(lèi)型不同
3. 引用的使用
(1) 引用作參數(shù)
例如: 交換兩個(gè)數(shù)
在C語(yǔ)言中,
void swap(int* x ,int* y)
{
int tmp = *x;
*x = *y;
*y = tmp;
}
int main()
{
int a = 10;
int b = 20;
swap(&a,&b); //把地址傳過(guò)去
return 0;
}
交換兩個(gè)數(shù)要把地址傳過(guò)去,形參是實(shí)參的臨時(shí)拷貝,只有把地址傳過(guò)去,參數(shù)才可以改變。
在C++中,使用引用,引用和原值是共用一塊內(nèi)存空間
void swap(int& x,int &y) //引用做參數(shù)
{
int tmp = x;
x = y;
y = tmp;
}
int main()
{
int a = 10;
int b = 20;
swap(a,b);
return 0;
}
這里的x和y分別是a和b的別名
上述是給整型取別名,也可以給指針取別名
//void PushBack(struct Node** pphead,int x){} //C語(yǔ)言使用二級(jí)指針
void PushBack(struct Node*& phead,int x) //C++,使用引用給指針取別名
{
//phead = newnode;
//...
}
小總結(jié):引用做參數(shù),a.做輸出型參數(shù) b.對(duì)象比較大時(shí),減少拷貝,提高效率。
(2) 引用作返回值
int& func() //返回值類(lèi)型 int& ,傳引用返回
{
int a = 1000;
return a;
}
int main()
{
int ret = func();
cout << ret << endl;
return 0;
}
int func 傳值返回時(shí)(返回的是拷貝),函數(shù)棧幀的銷(xiāo)毀,會(huì)導(dǎo)致a的值可能是隨機(jī)值。
int& func 傳引用返回,返回a的別名,但是指向的內(nèi)存空間沒(méi)有改變,函數(shù)棧幀的銷(xiāo)毀,內(nèi)存返回給操作系統(tǒng),引用的值也變成了隨機(jī)值。(出現(xiàn)了野引用)
什么情況下可以使用引用返回?
全局變量/靜態(tài)變量/堆上的空間等可以作引用返回
小總結(jié):
傳值返回,傳的是返回值的拷貝;傳引用返回,傳的是返回值的別名。
返回變量出了函數(shù)作用域,生命周期就到了要銷(xiāo)毀 (局部變量),不能用引用返回。
引用作返回值 a.修改返回對(duì)象 b.減少拷貝調(diào)高效率
4. 引用和指針的區(qū)別
int main()
{
//引用
int a = 10;
int& ra = a; //語(yǔ)法層面不開(kāi)空間
ra = 20;
//指針
int* pa = &a; //語(yǔ)法層面開(kāi)空間
*pa = 20;
return 0;
}
語(yǔ)法上(規(guī)定用法) :
(1) 引用是別名, 不開(kāi)空間,指針是地址, 需要開(kāi)辟內(nèi)存空間
(2) 引用必須初始化,指針可以初始化也可以不初始化
(3) 引用不能改變指向,指針可以改變指向
(4) 引用相對(duì)更安全,沒(méi)有空引用,但是有空指針,容易出現(xiàn)野指針,但不容易出現(xiàn)野引用
(5) 在sizeof中含義不同,引用結(jié)果引用類(lèi)型的大小,但指針始終是地址空間所占字節(jié)個(gè)數(shù)
(6) 引用++,引用的實(shí)體增加1,指針++即指針向后偏移一個(gè)類(lèi)型的大小
(7) 有多級(jí)指針,但是沒(méi)有多級(jí)引用
底層上(匯編) :
引用的底層使用指針實(shí)現(xiàn)的
匯編層面上,沒(méi)有引用,都是指針,引用編譯后也轉(zhuǎn)換成指針了。
引用和指針的共能類(lèi)似,但是引用不能完全替代指針,因?yàn)橐枚x后不能改變指向。例如:在單鏈表中,刪除鏈表中的某一個(gè)結(jié)點(diǎn),需要讓指針指向要?jiǎng)h除結(jié)點(diǎn)的下一個(gè)結(jié)點(diǎn)(改變指針的指向)。但是引用是無(wú)法進(jìn)行改變指向的。
然而在java和python中實(shí)現(xiàn)鏈表是通過(guò)引用,java和python中的引用時(shí)允許改變指向的。
六、 內(nèi)聯(lián)函數(shù)
內(nèi)聯(lián)函數(shù)概念
內(nèi)聯(lián)函數(shù):以inline修飾的函數(shù)叫做內(nèi)聯(lián)函數(shù),編譯時(shí)C++編譯器會(huì)在調(diào)用內(nèi)聯(lián)函數(shù)的地方展開(kāi),沒(méi)有函數(shù)調(diào)用建立棧幀的開(kāi)銷(xiāo),內(nèi)聯(lián)函數(shù)可以提升程序運(yùn)行的效率。
在C語(yǔ)言中,如果進(jìn)行多次調(diào)用函數(shù),那么會(huì)多次建立函數(shù)棧幀,C語(yǔ)言是通過(guò)宏函數(shù)來(lái)解決這個(gè)問(wèn)題。因?yàn)楹陼?huì)在預(yù)處理階段進(jìn)行宏的替換,這樣就不會(huì)建立函數(shù)棧幀了。
宏的優(yōu)點(diǎn):
沒(méi)有函數(shù)棧幀的創(chuàng)建,提供效率
但是宏也是有一些缺點(diǎn)
宏的缺點(diǎn): 語(yǔ)法復(fù)雜,坑很多,不容易控制;不能調(diào)試;沒(méi)有 類(lèi)型安全的檢查
C++替代宏的技術(shù):可以使用常量定義 (const enum) ; 短小函數(shù)定義 換用內(nèi)聯(lián)函數(shù)
為了減少使用宏,C++給出了內(nèi)聯(lián)函數(shù)
在C++中,在調(diào)用內(nèi)聯(lián)函數(shù)的地方展開(kāi),沒(méi)有函數(shù)調(diào)用建立棧幀的開(kāi)銷(xiāo)
為什么內(nèi)聯(lián)函數(shù)適合短小函數(shù)呢?
假設(shè) func函數(shù)有100行,項(xiàng)目中有1w個(gè)調(diào)用該函數(shù)的位置
內(nèi)聯(lián)展開(kāi)了 100*1w = 100w行
不使用內(nèi)聯(lián)展開(kāi) 100+1w = 1.01w行
大的函數(shù)就影響很大,所以 短小函數(shù)可以換用內(nèi)聯(lián)函數(shù)
在內(nèi)聯(lián)函數(shù)在長(zhǎng)一點(diǎn)的函數(shù)和遞歸函數(shù)是不會(huì)展開(kāi)的
內(nèi)聯(lián)函數(shù)特性
內(nèi)聯(lián)函數(shù)(inline)是一種以空間換時(shí)間的做法,在編譯階段,會(huì)把函數(shù)展開(kāi)。優(yōu)點(diǎn):減少了函數(shù)棧幀的開(kāi)銷(xiāo),提高效率;缺點(diǎn):目標(biāo)文件可能會(huì)增大。 內(nèi)聯(lián)函數(shù)適合較小代碼量的函數(shù) inline不建議聲明和定義分離,分離會(huì)導(dǎo)致鏈接錯(cuò)誤,因?yàn)閕nline被展開(kāi),就沒(méi)有函數(shù)地址,鏈接時(shí)的符號(hào)表就找不到了。 例如: //test.cpp
#include "test.h"
int main()
{
func();
return 0;
}
//test.h
#pragma once //避免頭文件被重復(fù)包含,可以提高運(yùn)行效率
#include
using namespace std;
inline void func(); //error
//func.cpp
#include "test.h"
void func()
{
cout << "func()" << endl;
}
因?yàn)閕nline函數(shù)展開(kāi),匯編call調(diào)用時(shí)沒(méi)有函數(shù)地址
如果要在test.h文件寫(xiě)函數(shù),代碼量較小的可以用內(nèi)聯(lián)(聲明和定義不分離),大的就使用static (鏈接屬性,只在當(dāng)前文件可見(jiàn))
七、auto關(guān)鍵字
在C++中,auto有許多作用,但是最常用的是用于類(lèi)型推導(dǎo)。
int i = 0;
auto k = i; //auto 在這里的作用就是自動(dòng)(識(shí)別)推導(dǎo)類(lèi)型
void(*pf1)(int, int) = func; //函數(shù)指針
auto pf2 = func; //自動(dòng)識(shí)別函數(shù)指針
//打印類(lèi)型進(jìn)行驗(yàn)證一下、
cout << typeid(pf1).name() << endl;//void (__cdecl*)(int,int)
cout << typeid(pf2).name() << endl;//void (__cdecl*)(int,int)
//std::map::iterator it = dict.begin();
//auto it = dict.begin();
當(dāng)然上述也可以使用C語(yǔ)言中的typedef 類(lèi)型重命名,但是當(dāng)計(jì)算出表達(dá)式的值的時(shí)候,進(jìn)行賦值( 就需要知道數(shù)據(jù)類(lèi)型 ),在一些項(xiàng)目中使用typedef沒(méi)有那么容易,但是 auto關(guān)鍵字可以進(jìn)行自動(dòng)推導(dǎo)。
auto從C++11標(biāo)準(zhǔn)及更高標(biāo)準(zhǔn)下可以作返回類(lèi)型
auto func(int a,int b) {}
注意:
用auto定義變量時(shí)必須對(duì)其進(jìn)行初始化,在編譯階段編譯器需要根據(jù)初始化表達(dá)式來(lái)推導(dǎo)auto的實(shí)際類(lèi)型。因此auto并非是一種“類(lèi)型”的聲明,而是一個(gè)類(lèi)型聲明時(shí)的“占位符”,編譯器在編譯期會(huì)將auto替換為變量實(shí)際的類(lèi)型。
auto的缺點(diǎn):
(1) auto不能作為函數(shù)的參數(shù)
// 此處代碼編譯失敗,auto不能作為形參類(lèi)型,因?yàn)榫幾g器無(wú)法對(duì)a的實(shí)際類(lèi)型進(jìn)行推導(dǎo)
void TestAuto(auto a){}
(2) auto不能用來(lái)直接聲明數(shù)組
int a[] = {1,2,3};
auto b[] = {4,5,6}; //error
八、 基于范圍的for循環(huán)(C++11)
范圍for的語(yǔ)法
C++11中引入了基于范圍的for循環(huán)。for循環(huán)后的括號(hào)由冒號(hào)“ :”分為兩部分:第一部分是范圍內(nèi)用于迭代的變量,第二部分則表示被迭代的范圍。
int a[] = {1,2,3,4,5,6};
int b[] = {1,2,3,4,5,6};
//普通的for循環(huán)
for (int i = 0; i < sizeof(a) / sizeof(a[0]);i++)
{
a[i] *= 2;
}
for (int i = 0;i< sizeof(a) / sizeof(a[0]);i++)
{
cout << *(a + i) <<' '; //打印結(jié)果:2 4 6 8 10 12
}
//范圍for的使用 (C++11)
for (int e:b) //依次取數(shù)組中賦值給e,自動(dòng)迭代,自動(dòng)判斷結(jié)束
e *= 2;
for (int e : b)
cout << e << ' '; //打印結(jié)果:1 2 3 4 5 6
上述范圍for的使用C++11也進(jìn)行了數(shù)值乘2,但是打印結(jié)果沒(méi)有發(fā)生變化。原因是: 因?yàn)槭菑臄?shù)組中取出賦值給e,并沒(méi)有改變數(shù)組中的數(shù)據(jù),所以打印還是原值。
要想修改原數(shù)組的值,則需要使用引用
for (int& e : b) //依次取數(shù)組中賦值給e,自動(dòng)迭代,自動(dòng)判斷結(jié)束
e *= 2;
for (int e : b)
cout << e << ' '; //打印結(jié)果2 4 6 8 10 12
//當(dāng)然也可以換成auto
for (auto& e : b) //依次取數(shù)組中賦值給e,自動(dòng)迭代,自動(dòng)判斷結(jié)束
e *= 2;
for (auto e : b)
cout << e << ' '; //打印結(jié)果2 4 6 8 10 12
范圍for循環(huán)可以用continue來(lái)結(jié)束本次循環(huán),也可以用break來(lái)跳出整個(gè)循環(huán)。
for循環(huán)迭代的范圍必須是確定的
九、 指針空值
聲明一個(gè)變量及時(shí)初始化是一個(gè)C/C++的一個(gè)好的編程習(xí)慣,否則可能會(huì)出現(xiàn)不可預(yù)料的錯(cuò)誤,例如未初始化指針。
//初始化指針
int *p1 = NULL;
int* p2 = 0;
然而在C++98標(biāo)準(zhǔn)中出現(xiàn)了關(guān)于指針的bug
void f(int i)
{
cout << "f(int)" << endl;
}
void f(int* i)
{
cout << "f(int*)" << endl;
}
int main()
{
f(0);
f(NULL);
return 0;
}
打印結(jié)果:
f(int)
f(int)
發(fā)現(xiàn)都調(diào)用了第一個(gè)函數(shù)
分析:
f((int *)NULL); //打印 f(int*)
隨后在C++11標(biāo)準(zhǔn)中出現(xiàn)了 nullptr
f(nullptr); //打印 f(int*)
int* p = nullptr;
nullptr 的主要優(yōu)勢(shì) 在于它提供了更好的類(lèi)型安全性。它只能被賦值給指針類(lèi)型,而不能被賦值給整數(shù)類(lèi)型,這有助于減少因錯(cuò)誤地將NULL或0用于非指針類(lèi)型而引起的問(wèn)題。此外,使用 nullptr 可以避免與某些平臺(tái)上的空指針值沖突。
在C++11標(biāo)準(zhǔn)中,sizeof(nullptr) 和sizeof((void*)0)所占字節(jié)數(shù)相同,后續(xù)表示指針空值時(shí),建議使用nullptr
12
范圍for循環(huán)可以用continue來(lái)結(jié)束本次循環(huán),也可以用break來(lái)跳出整個(gè)循環(huán)。
for循環(huán)迭代的范圍必須是確定的
# 九、 指針空值
聲明一個(gè)變量及時(shí)初始化是一個(gè)C/C++的一個(gè)好的編程習(xí)慣,否則可能會(huì)出現(xiàn)不可預(yù)料的錯(cuò)誤,例如未初始化指針。
```cpp
//初始化指針
int *p1 = NULL;
int* p2 = 0;
然而在C++98標(biāo)準(zhǔn)中出現(xiàn)了關(guān)于指針的bug
void f(int i)
{
cout << "f(int)" << endl;
}
void f(int* i)
{
cout << "f(int*)" << endl;
}
int main()
{
f(0);
f(NULL);
return 0;
}
打印結(jié)果:
f(int)
f(int)
發(fā)現(xiàn)都調(diào)用了第一個(gè)函數(shù)
分析:
[外鏈圖片轉(zhuǎn)存中…(img-tjxJy0Wa-1706927812224)]
f((int *)NULL); //打印 f(int*)
隨后在C++11標(biāo)準(zhǔn)中出現(xiàn)了 nullptr
f(nullptr); //打印 f(int*)
int* p = nullptr;
nullptr 的主要優(yōu)勢(shì) 在于它提供了更好的類(lèi)型安全性。它只能被賦值給指針類(lèi)型,而不能被賦值給整數(shù)類(lèi)型,這有助于減少因錯(cuò)誤地將NULL或0用于非指針類(lèi)型而引起的問(wèn)題。此外,使用 nullptr 可以避免與某些平臺(tái)上的空指針值沖突。
在C++11標(biāo)準(zhǔn)中,sizeof(nullptr) 和sizeof((void*)0)所占字節(jié)數(shù)相同,后續(xù)表示指針空值時(shí),建議使用nullptr
柚子快報(bào)邀請(qǐng)碼778899分享:算法 初識(shí)cpp【C++】
http://yzkb.51969.com/
好文鏈接
評(píng)論可見(jiàn),查看隱藏內(nèi)容