小凱15天快速講完c語言-簡單學習第八課

瞿小凱發表於2022-11-24

0.前言

今天,我們進入c++的學習,我在專欄裡提到過,這些課程,來自我在大學自學時候的筆記整理而成,可能有不完善之處,在今天的課程筆記裡,我們忽略了一個有興趣的帶入點,c++的起源,在此引用維基百科的解釋

圖片.png

1.從C語言到C++一些基礎語法的變化

1.1 記憶體的申請和釋放

在C語言當中,我們學習的堆空間申請和釋放:
申請:malloc
釋放:free
在C++當中,推薦使用:
申請:new
釋放:delete

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
int main() 
{
    //1. 在C中,申請10個int的空間
    int* p1 = (int*)malloc(sizeof(int) * 10);
    memset(p1, 0, sizeof(int) * 10);

    //2. 在C++中,申請的方式,使用new,並且在申請的同時可以直接
    //初始化
    //注意:這種寫法,申請了10個int,前5個初始值是1,2,3,4,5
    //後面5個就是0
    int* p2 = new int[10]{1,2,3,4,5};
    //注意:這種寫法,申請了1個int,初始值是10
    int* p3 = new int(10);

    //3. 釋放malloc申請的空間
    free(p1);
    //4. 釋放new出來的空間
    // 當初申請的時候,申請了1個以上,釋放的時候就需要加[]
    delete[]p2;
    // 當初申請的時候,只申請了1個,釋放就無需加[]
    delete p3;
  }

他們有什麼區別?
1.malloc和free 他們是函式,new和delete 他們是C++的運算子
2.malloc返回的是一個 void*型別的指標,需要我們自己轉換的。new申請什麼型別就得到什麼型別的指標,不需要強制轉換的。
3.malloc不會呼叫類的建構函式,free不會呼叫類的解構函式。new會呼叫建構函式,delete會呼叫解構函式。
4.C++推薦使用new和delete

1.2 函式的過載


//實現一個函式,得到兩個整型資料的較大值
int GetMaxInt(int a, int b)
{
    if (a>b)
    {
        return a;
    }
    else
    {
        return b;
    }
}
//又有新需求,得到兩個浮點型資料的較大值
double GetMaxDouble(double a, double b)
{
    if (a > b)
    {
        return a;
    }
    else
    {
        return b;
    }
}

int main()
{
    int n = GetMaxInt(10, 20);
    double m = GetMaxDouble(10.5, 7.8);
    return 0;
}

上面這個程式碼,也是可以的。但是兩個函式的功能實際是一致的,都是獲取較大值。但是函式名卻不一樣,那麼就會增大我們記憶的負擔。需要記住很多的函式名。
C++提供了一個比較好的機制,可以減輕這樣的負擔------- 函式過載
函式過載:在相同的作用域內,函式的名字相同,但是引數不同,這樣可以構成過載,構成過載之後,在呼叫函式的時候,會根據傳遞的引數,自動選擇呼叫哪個函式。
引數不同:
a.型別不同
b.順序不同
c.個數不同

//實現一個函式,得到兩個整型資料的較大值
int GetMax(int a, int b)
{
    if (a > b)
    {
        return a;
    }
    else
    {
        return b;
    }
}
//又有新需求,得到兩個浮點型資料的較大值
double GetMax(double a, double b)
{
    if (a > b)
    {
        return a;
    }
    else
    {
        return b;
    }
}

int main()
{
    GetMax(20, 10);
    GetMax(20.8, 10.5);
    return 0;
}

只有返回值型別不同,不能構成過載的:
圖片.png
使用函式過載的好處:
我們不需要去維護,記憶很多的函式名,使用起來比較便利。
實際上,過載是一種多型機制,介面複用

名稱粉碎機制,C++的函式名,也要把引數型別算進去,是過載的底層機制。
圖片.png
如果不要名稱粉碎的話,可以在函式的前面 加上
extern "C" 這樣一個宣告,就會以 C的方式編譯函式,不會名稱粉碎了,也就不能過載了。

1.3 預設引數

//獲取自由落體的速度

double GetV(double t, double g = 9.8)
{
    return t * g;
}


int main()
{
    double v = GetV(5);
    GetV(10);
    GetV(20);
    GetV(20, 9.8 / 6);
    return 0;
}

一些需要注意的地方:
1.預設值只能從右往左設定,中間不能間斷
圖片.png

2.當一個函式既有宣告,又有定義的時候,預設引數只能寫在宣告中。
圖片.png

3.當同時出現預設引數和函式過載的時候,容易造成二義性問題

#include <stdio.h>

int GetAdd(int a, int b, int c, int d = 0, int e = 0)
{
    return a + b + c + d + e;
}
int GetAdd(int a, int b, int c)
{
    return a + b + c;
}
int main()
{
    GetAdd(1, 2, 3);
    return 0;
}

圖片.png

1.4 引用

#include <stdio.h>
int main()
{
    //1. 定義一個變數
    int nNum = 100;
    //2. 定義一個引用,引用nNum
    //這裡& 不是取地址,而是用於定義型別的
    int& a = nNum;//a就是nNum的別名
    a = 200;
    printf("%d %d\n", a, nNum);
    nNum = 500;
    printf("%d %d\n", a, nNum);
    //背後的原理是什麼呢??
    //a只是一個名字,沒有自己的空間,和被引用的物件nNum公用記憶體
    printf("%p %p", &a, &nNum);
    return 0;
}

有什麼用???
可以代替指標的一部分功能:

#include <stdio.h>
void swap(int& a, int& b)
{
    int n = a;
    a = b;
    b = n;
}
int main()
{
    int nNum1 = 10;
    int nNum2 = 20;
    swap(nNum1, nNum2);
    printf("%d %d", nNum1, nNum2);
    return 0;
}

引用能夠做到的事情,指標也是可以的。為什麼要使用引用呢???
引用和指標有什麼區別???(整體來說,引用比較安全)
1.引用必須初始化,指標可以不初始化。
2.引用一旦初始化,就不能引用其他位置了。
3.指標是一個變數,有自己的記憶體,引用是一個別名,沒有自己的記憶體

#include <stdio.h>
int main()
{
    //1. 引用必須要初始化,指標不必
    int nNum = 100;
    int& a = nNum;
    int* p = nullptr;
    p = &nNum;
    //2. 引用一經初始化,就不能再引用其他位置了
    int nNum2 = 200;
    a = nNum2;//這個叫複製
    p = &nNum2; //p指向了新的位置
    //3. 指標的本質是一個變數,有名字,有自己的空間(用來存地址的)
    //   引用是沒有自己的空間的,是依賴於被引用的物件存在而存在的
    return 0;
}

目前,我們們學習了3種傳參方式:
1.數值傳遞
2.指標傳遞:本質上,還是數值傳遞,只是這個數值是個地址。
3.引用傳遞:形參和實參共享記憶體,這裡就可以說 是形參改變了實參

1.5 C++的輸入和輸出

在C語言種,我們使用的是printf和scanf_s 實現的輸入和輸出。在C++中有了新的方式:
輸出:cout
輸入:cin
配合流運算子: 輸出流 << 輸入流 >>
不能直接使用
圖片.png

1.5.1 名稱空間

需要透過名稱空間去使用名稱空間:是一種防止命名衝突的機制有三種方式:using namespace std; //直接開啟名稱空間中的所有內容,全部都能直接使用了
圖片
using std::cout  ;//只開啟了 cout  使用誰開啟誰
圖片
使用cout的時候,加上名稱空間的名字, 使用 ::  作用域符號
圖片

一般使用 方式3或者方式2,方式1 不推薦

#include <iostream>
//using namespace std;
//using std::cout;
int main()
{
    std::cout << "helloworld"<<std::endl;
    std::cout << 10 << std::endl;
    std::cout <<3.32453245+8 << std::endl<<10<<20<<300<<'a';
    return 0;
}

圖片.png

1.5.2 cin的使用

#include <iostream>
//using namespace std;
using std::cin;

int main()
{
    //1. 輸入一個整數
    int nNum = 0;
    cin >> nNum;
    //2. 輸入一個字元
    char cCh = 0;
    cin >> cCh;
    //3. 輸入一個小數
    double fNum = 0;
    cin >> fNum;
    //4. 輸入一個字串
    char buf[50] = {};
    char* p = new char[100]{ 0 };
    //gets_s(buf); 可以接收空格
    cin >> buf;
    cin >> p;
    return 0;
}

2.類的基本語法

2.1 理解類的語法

定義學生結構體,並且能夠進行結構體的一些使用

#include <iostream>
//using namespace std;
using std::cin;

struct STUDENT {
    char  szName[20]; //姓名
    int nId;          //學號
    int nScore;       //分數
};

void PrintfStu(STUDENT stu)
{
    printf("%s ", stu.szName);
    printf("%d ", stu.nId);
    printf("%d ", stu.nScore);
}

void PrintfStu(STUDENT* pstu)
{
    printf("%s ", pstu->szName);
    printf("%d ", pstu->nId);
    printf("%d ", pstu->nScore);
}
void SetStu(STUDENT* pstu,const char* szName,int nId,int nScore)
{
    //pstu->szName = szName
    strcpy_s(pstu->szName, szName);
    pstu->nId = nId;
    pstu->nScore = nScore;
}
int main()
{
    STUDENT stu1 = { "xiaoming",20,90 };
    PrintfStu(&stu1);
    SetStu(&stu1, "xiaohong", 21, 95);
    PrintfStu(&stu1);

    return 0;
}

在函式傳參的時候,如果引數需要是結構體的話,一般使用指標,優點有兩個:
1.傳遞的資料量比較小的,只有4個位元組
2.能夠修改外部的資料

以上的程式碼,都是學習過的,在C語言程式開發中,也是沒有問題的。
但是,這裡有一個天然的缺點:
需要由程式設計師自己去維護函式和變數之間的使用關係,比如:
我們需要很清楚 PrintfStu,SetStu使用的是STUDENT這個結構體。
在程式規模比較小的時候,這個是比較好維護的。
當程式規模很大之後,再去維護他們的關係就是一個比較大的負擔了。比如有好幾百個結構體,好幾千個函式。
此時有一個新的語法,能解決這個問題,就是類。

2.2 類的語法

#include <iostream>
//關鍵字:class
//類名:一般以C開頭,後面是一個單詞,首字母大寫
//類和結構體一樣,都是自定義的資料型別
//這個資料型別中,可以包含變數,也可以包含函式
class CStudent {
public:
    void PrintfStu()
    {
        printf("%s ", this->szName);
        printf("%d ", this->nId);
        printf("%d ", this->nScore);
    }
    void SetStu( const char* szName, int nId, int nScore)
    {
        //pstu->szName = szName
        strcpy_s(this->szName, szName);
        this->nId = nId;
        this->nScore = nScore;
    }
private:
    char  szName[20]; //姓名
    int nId;          //學號
    int nScore;       //分數
};
int main()
{
    //定義的這個變數,即包含了資料,又能直接使用函式
    CStudent stu1 ;
    stu1.SetStu("xiaoming", 20, 90);
    stu1.PrintfStu();
    stu1.SetStu("xiaohong", 21, 95);
    return 0;
}

總結:
1.這麼寫了之後,資料和操作這個資料的函式,在語法上就有了聯絡,他們之間的關係就不需要我們維護了。
2.類:class定義出來的新型別 物件:使用類定義的變數
3.類中的函式,稱之為 成員函式 或者 成員方法 方法 行為
4.類中的變數,稱之為 成員變數 或者 屬性 類中資料

2.3 類中的許可權:

類中的資料和函式,有一個許可權的概念:
訪問物件中的資料,通常有兩種方式:
1.透過物件直接訪問
2.透過物件呼叫物件自己的函式,由物件自己的函式去訪問(自己的函式訪問自己的資料)
public(公有的): 可以透過類的物件 直接去使用的成員
protected(保護的):不可以透過類的物件 直接去使用的成員 (在繼承的時候再詳細講)
private(私有的): 不可以透過類的物件 直接去使用的成員
許可權體現的也是封裝性的思想:
通常來說,將資料定義為私有,這樣的話,使用類的人就不能隨意的修改資料,更為安全。具體怎麼使用這些資料,都需要透過類提供的函式。整個程式就會更為安全。

2.4 this指標

當我們定義一個物件的時候,只定義出來了新的資料成員。函式在記憶體永遠都只有一份
圖片.png

在SetStu這個函式中,如何區分要修改哪一個物件的資料???就是透過this指標。
this指標本質上來說,就是物件的地址。是預設傳遞進去的。

#include <iostream>
//關鍵字:class
//類名:一般以C開頭,後面是一個單詞,首字母大寫
//類和結構體一樣,都是自定義的資料型別
//這個資料型別中,可以包含變數,也可以包含函式
class CStudent {
public:
    void PrintfStu()
    {
        printf("%s ", this->szName);
        printf("%d ", this->nId);
        printf("%d ", this->nScore);
    }
    void SetStu( const char* szName, int nId, int nScore)
    {
        //pstu->szName = szName
        strcpy_s(this->szName, szName);
        this->nId = nId;
        this->nScore = nScore;
    }
private:
    char  szName[20]; //姓名
private:
    int nId;          //學號
public:
    int nScore;       //分數
};
int main()
{
    //定義的這個變數,即包含了資料,又能直接使用函式
    CStudent stu1 ;
    CStudent stu2;
    CStudent stu3;
    stu1.SetStu("xiaoming", 20, 90);//stu1.SetStu(&stu1,"xiaoming", 20, 90);
    stu1.PrintfStu();//stu1.PrintfStu(&stu1);
    stu1.SetStu("xiaohong", 21, 95);

    stu1.nScore = 50;
    stu2.SetStu("xiaobai", 15, 88);//stu2.SetStu(&stu2,"xiaobai", 15, 88);
    return 0;
}

2.5 類中函式的編寫位置

我們們演示的時候,把函式寫在了類中。
但是實際情況,一般把函式都是寫在類外的

圖片

圖片

相關文章