博客 / 詳情

返回

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

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 類中函數的編寫位置

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

圖片

圖片

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.