C++基礎(chǔ) | C++對C語言的拓展

引用

引用相當(dāng)于常指針
如 int &a和int* const a

引用的規(guī)則

1 引用沒有定義,是一種關(guān)系型聲明。聲明它和原有某一變量(實體)的關(guān)系。故而類型與原類型保持一致,且不分配內(nèi)存。與被引用的變量有相同的地 址。
2 聲明的時候必須初始化,一經(jīng)聲明,不可變更。
3 可對引用,再次引用。多次引用的結(jié)果,是某一變量具有多個別名。
4 &符號前有數(shù)據(jù)類型時,是引用。其它皆為取地址。

int main(void)
{
int a,b;
int &r = a;
int &r = b; //錯誤,不可更改原有的引用關(guān)系
float &rr = b; //錯誤,引 類型不匹配 cout<<&a<<&r<<endl; 
//變量與引用具有相同的地址。
int &ra = r; //可對引用更次引用,表 a 變量有兩個別名,分別是 r 和 ra
return 0; 
}
引用的意義

1)引用作為其它變量的別名而存在,因此在一些場合可以代替指針
2)引用相對于指針來說具有更好的可讀性和實用性

#include<iostream>
using namespace std;
void swap1(int &a, int &b){
    int tmp;
    tmp = a; a = b;
    b = tmp;
}
void swap2(int *a,int *b){
    int tmp;
    tmp=*a;
    *a=*b;
    *b=tmp;
}
int main() {
    int a = 3,b = 5;
    cout<<"a = "<<a<<"b = "<<b<<endl;
    swap1(a,b);
    cout<<"a = "<<a<<"b = "<<b<<endl;
    swap2(&a,&b);
    cout<<"a = "<<a<<"b = "<<b<<endl;
    return 0;
}

運行結(jié)果
a = 3b = 5
a = 5b = 3
a = 3b = 5
引用作為函數(shù)的返回值(引用當(dāng)左值)
#include <iostream>
using namespace std;
int& getA1() {
    int a;
    a = 10;
    return a;
}
int& getA2(int &a) {
    a = 10;
    return a;
}

int main(void)
{
    int a1 = 0;
    int a2 = 0;

    int &a3 = getA1();

    cout <<"a3 = " <<a3<<endl; //結(jié)果為32767,由于是棧的引 ,內(nèi)存非法
    //getA1返回的不應(yīng)該是形參

    int &a4 = getA2(a1); //這樣的才可以正確返回引用,結(jié)果為10

    cout <<"a4 = " <<a4<<endl; 
    
   
return 0; 
}
指針引用
#include <iostream>
using namespace std;
struct Teacher
{
    char name[64];
int age ; 
};
int getTeacher(Teacher **p) {
    Teacher *tmp = NULL;
    if (p == NULL){
        return -1;
    }
    tmp = (Teacher *)malloc(sizeof(Teacher));
    if (tmp == NULL){
        return -2;
    }
    tmp->age = 33;  
    // p是實參的地址 *實參的地址 去間接的修改實參的值 
    *p = tmp;
    return 0; 
}
//指針的引用做函數(shù)參數(shù)
int getTeacher2(Teacher* &myp) {
//給myp賦值 相當(dāng)于給main函數(shù)中的pT1賦值
    myp = (Teacher *)malloc(sizeof(Teacher)); 
    if (myp == NULL){
    return -1; 
    }
    myp->age = 36;
    return 0;
}
void FreeTeacher(Teacher *pT1){
    if (pT1 == NULL){
    return ; 
    }
    free(pT1); 
}

//getTeacher和getTeacher2效果一致
int main(void)
{
    Teacher *pT1 = NULL;

    getTeacher(&pT1); 
    cout<<"age:"<<pT1->age<<endl; 
    FreeTeacher(pT1);
//引用的本質(zhì) 間接賦值后2個條件 讓c++編譯器幫我們程序員做了。
 
    getTeacher2(pT1);
    cout<<"age:"<<pT1->age<<endl;
    FreeTeacher(pT1);
return 0; 
}

const引用

const 引用有較多使用。它可以防止對象的值被隨意修改。因而具有一 些特性。
(1)const 對象的引用必須是 const 的,將普通引用綁定到 const 對象是不 合法的。這個原因比較簡單。既然對象是 const 的,表示不能被修改,引用當(dāng)然 也不 能修改,必須使用 const 引用。實際上,
const int a=1;
int &b=a;
這種寫法是不合法 的,編譯不過。
(2)const 引用可使用相關(guān)類型的對象(常量,非同類型的變量或表達式)初 始化。這個是 const 引用與普通引用最大的區(qū)別。
是合法的。

const int &a=2;
double x=3.14;
const int &b=a;
const int &b=x;(這里要注意,因為不是同類型,此時b的地址和x的地址已經(jīng)不一樣了,開辟了一個空間存放強制轉(zhuǎn)換后的值)

#include <iostream>
using namespace std;

int main(void)
{
double val = 3.14;
const int &ref = val;
double & ref2 = val;
cout<<ref<<" "<<ref2<<endl;//3 3.14
val = 4.14;
cout<<ref<<" "<<ref2<<endl;//3 4.14
return 0; 
}

結(jié)論:
1)const int & e 相當(dāng)于 const int * const e
2)普通引用 相當(dāng)于 int *const e
3)當(dāng)使用常量(字面量)對const引用進行初始化時,C++編譯器會為常量值 分配空間,并將引用名作為這段空間的別名
4)使用字面量對const引用初始化后,將生成一個只讀變量
const int &ref =3;//地址0x00007fff5fbffe5c

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

#include <iostream>
using namespace std;
inline void func(int a)
{
a = 20;
    cout << a <<endl;
}
int main(void)
{
func(10);
 /*
//編譯器將內(nèi)聯(lián)函數(shù)的函數(shù)體直接展開
 {
        a = 20;
        cout << a <<endl;
  }
*/
return 0; 
}

特點:
1)內(nèi)聯(lián)函數(shù)聲明時inline關(guān)鍵字必須和函數(shù)定義結(jié)合在一起,否則編譯器會直 接忽略內(nèi)聯(lián)請求。
2)C++編譯器直接將函數(shù)體插入在函數(shù)調(diào)用的地方 。
3)內(nèi)聯(lián)函數(shù)沒有普通函數(shù)調(diào)用時的額外開銷(壓棧,跳轉(zhuǎn),返回)。
4)內(nèi)聯(lián)函數(shù)是一種特殊的函數(shù),具有普通函數(shù)的特征(參數(shù)檢查,返回類型 等)。

  1. 內(nèi)聯(lián)函數(shù)由 編譯器處理,直接將編譯后的函數(shù)體插入調(diào)用的地方,宏代碼片段 由預(yù)處理器處理, 進行簡單的文本替換,沒有任何編譯過程(可能會有bug)。
C++中內(nèi)聯(lián)編譯的限制

不能存在任何形式的循環(huán)語句
不能存在過多的條件判斷語句
函數(shù)體不能過于龐大
不能對函數(shù)進行取址操作
函數(shù)內(nèi)聯(lián)聲明必須在調(diào)用語句之前

編譯器對于內(nèi)聯(lián)函數(shù)的限制并不是絕對的,內(nèi)聯(lián)函數(shù)相對于普通函數(shù)的優(yōu) 勢只是省去了函數(shù)調(diào)用時壓棧,跳轉(zhuǎn)和返回的開銷。因此,當(dāng)函數(shù)體的執(zhí)行開 銷遠大于壓棧,跳轉(zhuǎn)和返回所用的開銷時,那么內(nèi)聯(lián)將無意義。

優(yōu)點:避免調(diào)用時的額外開銷(入棧與出棧操作)
代價:由于內(nèi)聯(lián)函數(shù)的函數(shù)體在代碼段中會出現(xiàn)多個“副本”,因此會增加代碼 段的空間。
本質(zhì):以犧牲代碼段空間為代價,提高程序的運行時間的效率。
適用場景:函數(shù)體很“小”,且被“頻繁”調(diào)用。

內(nèi)聯(lián)函數(shù)一般很少用

默認參數(shù)和占位參數(shù)

默認參數(shù)

單個默認參數(shù)時:

//1 若 你填寫參數(shù),使 你填寫的,不填寫默認 
void myPrint(int x = 3)
{
    cout<<"x: “<<x<< endl;
}

多個默認參數(shù)時,一旦某個參數(shù)使用默認參數(shù),后面的都要使用

占位參數(shù)
#include <iostream>
/*
  函數(shù)占位參數(shù)
  占位參數(shù)只有參數(shù)類型聲明, 沒有參數(shù)名聲明
   般情況下,在函數(shù)體內(nèi)部 法使 占位參數(shù)
*/
int func(int a, int b, int)
{
return a + b; }
int main() {
    func(1, 2); //error, 必須把最后 個占位參數(shù)補上。 
    printf("func(1, 2, 3) = %d\n", func(1, 2, 3));
    return 0;
}
//C++可以聲明占位符參數(shù),占位符參數(shù)一般般用于程序擴展和對C代碼的兼容

重載

函數(shù)形參列表(參數(shù)的個數(shù),參數(shù)的類型,參數(shù)順序)
構(gòu)成函數(shù)重載的條件是:函數(shù)名相同,函數(shù)形參列表不同。
返回值的類型不是條件。

編譯器調(diào)用重載函數(shù)的準(zhǔn)則:

1.將所有同名函數(shù)作為候選者
2.嘗試尋找可行的候選函數(shù)
3.精確匹配實參
4.通過默認參數(shù)能夠匹配實參
5.通過默認類型轉(zhuǎn)換匹配實參
6.匹配失敗
7.最終尋找到的可行候選函數(shù)不唯一,則出現(xiàn)二義性,編譯失敗。
(若形參涉及默認參數(shù),可能會導(dǎo)致二義性)
8.無法匹配所有候選者,函數(shù)未定義,編譯失敗。

重載底層實現(xiàn)

C++利用 name mangling(傾軋)技術(shù),來改名函數(shù)名,區(qū)分參數(shù)不同的同名函數(shù)。
實現(xiàn)原理:用 v c i f l d 表示 void char int float long double 及其引用。

void func(char a);                            // func_c(char a)
void func(char a, int b, double c); //func_cid(char a, int b, double c)
函數(shù)指針的基本語法

插入typedef的一些用法:
typedef int INT_ARRAY_10[10]; //定義一個長度為10的int數(shù)組
//別名為INT_ARRAY_10
如INT_ARRAY_10 a;

為函數(shù)指針定義新的名稱
typedef int (*MyFUN)(int a,intb);
MyFUN是指向函數(shù)的指針類型的新別名,int是函數(shù)返回類型
(int a, int b)是參數(shù)列表

(找時間好好研究更多有關(guān)細節(jié)https://www.cnblogs.com/seventhsaint/archive/2012/11/18/2805660.html

// 法 一:
//聲明一 個函數(shù)類型
typedef void (myTypeFunc)(int a,int b);
//定義 一個函數(shù)指針
myTypeFunc *myfuncp = NULL; //定義一個函數(shù)指針 這個指針指向函數(shù)的  地址
// 法二 :
//聲明 一個函數(shù)指針類型
typedef void (*myPTypeFunc)(int a,int b) ; //聲明了一個指針的數(shù)據(jù)類型
 //定義一個函數(shù)指針
myPTypeFunc fp = NULL; //通過 函數(shù)指針類型 定義了 一個函數(shù)指針 ,
// 法三:
//定義一個函數(shù)指針 變量
void (*myVarPFunc)(int a, int b) = NULL;
函數(shù)指針與函數(shù)重載的結(jié)合
int func(int x) // int(int a)
{
return x;
34
}
int func(int a, int b)
{
return a + b; }
int func(const char* s)
{
   return strlen(s);
}
typedef int(*PFUNC)(int a);             // int(*)(int a)
typedef int(*PFUNC2)(int a, int b); // int(*)(int a, int b)

函數(shù)重載總結(jié):
重載函數(shù)在本質(zhì)上是相互獨立的不同函數(shù)。(在底層命名是不同的)
函數(shù)的函數(shù)類型是不同的
函數(shù)返回值不能作為函數(shù)重載的依據(jù)
函數(shù)重載是由函數(shù)名和參數(shù)列表決定的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容