主頁 > 後端開發 > 當初自學C++時的筆記記錄

當初自學C++時的筆記記錄

2021-04-05 06:04:44 後端開發

編輯:劉風琛

最初撰寫日期:2020年4月11日下午 最新更新日期:2020年9月20日上午

標注:

  • 從筆記開始截止到程式第四章“程式流程結構”,使用Joplin撰寫,其余部分為Typora撰寫,
  • 筆記對應課程鏈接為:(https://www.bilibili.com/video/BV1et411b73Z) 作者:黑馬程式員-
  • 當前進度為P107:類和物件

目錄
  • 1. 變數
  • 2. 常量
  • 3. 資料型別
    • 3.1 整型
    • 3.2 浮點型(小數)
    • 3.3 字符型
    • 3.4 字串
    • 3.5 布爾型別
  • 3. 運算子
  • 4. 程式流程結構
    • 4.1 順序結構
    • 4.2 選擇結構
    • 4.3 回圈結構
      • 4.3.1 while陳述句
      • 4.3.2 do...while陳述句
      • 4.3.3 for回圈陳述句
    • 4.4 跳轉陳述句
      • 4.4.1 break陳述句
      • 4.4.2 continue陳述句
      • 4.4.3 goto陳述句
  • 5. 陣列
    • 5.1 一維陣列
    • 5.2 二維陣列
  • 6. 函式
    • 6.1函式的定義
    • 6.2 函式的呼叫
    • 6.3 函式的宣告
    • 6.4 函式的分檔案撰寫
    • 6.5 函式的默認值
    • 6.6 函式的占位引數
    • 6.7 函式的多載
      • 6.7.1 概述
      • 6.7.2 函式多載的細節問題
  • 7. 指標
    • 7.1 指標的定義
    • 7.2 指標的使用
    • 7.3 空指標
    • 7.4 野指標
    • 7.5 const修飾指標
    • 7.6 指標和陣列
    • 7.7 指標和函式
    • # 指標、函式、陣列的搭配示例
  • 8. 結構體
    • 8.1 定義和使用
    • 8.2 結構體陣列
    • 8.3 結構體指標
    • 8.4 結構體嵌套
    • 8.5 將結構體作為函式引數傳遞
  • 9. 聯系人管理系統實體
  • 10 程式的記憶體模型
    • 10.1 記憶體磁區模型
    • 10.2 磁區意義
    • 10.3 程式運行前
    • 10.4 程式運行后
  • 11. C++中的參考
    • 11.1 參考的基本使用
    • 11.2 參考作函式引數
    • 11.2 參考作函式的回傳值
    • 11.3 常量參考
  • 12. 類和物件
    • 12.1 封裝
      • 12.1.1 封裝的意義
      • 12.1.2 訪問權限控制
      • 12.1.3 成員屬性私有化
    • 12.2 物件的初始化和清理
      • 12.2.1 建構式和解構式
      • 12.2.2 建構式的分類以及呼叫
      • 12.2.3 拷貝建構式的呼叫時機
      • 12.2.4 建構式的呼叫規則
      • 12.2.5 深拷貝與淺拷貝
      • 12.2.6 初始化串列
      • 12.2.7 類物件作為類成員
      • 12.2.8 靜態成員
    • 12.3 C++物件模型和this指標
      • 12.3.1 成員變數和成員函式分開儲存
      • 12.3.2 this指標概念
      • 12.3.3 空指標呼叫成員函式
      • 12.3.4 const修飾成員函式
    • 12.4 友元
    • 12.5 運算子的多載
      • 12.5.1 加號運算子的多載
    • 12.6 繼承
  • 13. 檔案操作
    • 13.1文本檔案
      • 13.1.1寫檔案的基本操作
      • 13.1.2讀檔案的基本操作
  • 14. C++中的STL
    • 14.1 STL的基本概念
    • 14.2 vector容器的基本使用
    • 14.3 string容器的基操
  • # 其他內容
    • 亂數生成
    • 記憶體
    • 靜態變數

1. 變數

給一段記憶體起名,方便使用,

2. 常量

用于記錄程式中不可更改的資料,

  • 定義常量的兩種方式
    1. #define宏常量 #define 常量名 常量值
      • 通常在檔案上方定義,表示一個常量,
    2. const修飾的變數 const 資料型別 常量名=常量值
      • 通常在變數定義前加const,修飾該變數為常量,不可更改,`

3. 資料型別

sizeof()可以回傳當前資料型別所占記憶體大小,

  • 強制轉換
    語法:(資料型別)被轉變數
    舉例:

    int main(){
    	char ch = 'a';
    	cout<<(int)ch<<endl;
    	return 0;
    }
    

    輸出結果:97(字符a的ASCII碼)

  • 轉義字符
    轉義字符用反斜杠\表示,可以用來表示ASCII碼的特殊值,

轉義字符 含義 ASCII碼值
\a 警報 007
\b 退格(BS),將當前位置移到前一列 008
\f 換頁(FF),將當前位置移到下頁開頭 012
\n 換行(LF),將當前位置移到下一行開頭 010
\r 回車(CR),將當前位置移到本行開頭 013

3.1 整型

C++中可以用以下方式表示整型,區別在于所占記憶體空間不同

資料型別 占用空間 取值范圍
short(短整型) 2位元組 -215~215-1
int(整型) 4位元組 -231~231-1
long(長整型) windows為4位元組;Linux 32位4位元組,64位8位元組 -231~231-1
long long(長長整型) 8位元組 -263~263-1

3.2 浮點型(小數)

浮點型分為以下兩種:

資料型別 占用空間 取值范圍
float(單精度) 4位元組 7位有效數字
double(雙精度) 8位元組 15~16位有效數字
  • 科學計數法
    舉例:
    整數:3e1表示3*10^1,也就是30
    小數:3e-1表示3*10^-1,也就是0.3

3.3 字符型

表示單個字符的資料型別,只占一個位元組,

  • 語法:char ch = 'a'
  • 注意:
    • 字符需要用單引號括起,
    • 且單引號中只能有一個字符,
    • 計算機真正存放的不是字符,是ASCII碼,

3.4 字串

表示一串字符,可以有兩種表示方式,

  • C語言中常用方式(陣列):char 變數名[] = "abcde"
    示例:

    int main(){
    	char str[] = "Hello world!";
    	cout<<str<<endl;
    	return 0;
    }
    
    • 注意:
      1. 字串內容要用單引號括起來,
      2. 變數名后必須加中括號表示陣列,
  • 當前標準方式:string 變數名 = "abcde"
    示例:

    #include <string>
    int main(){
    	string str = "Hello World!";
    	cout<<str<<endl;
    	return 0;
    }
    
    • 注意:
      1. 使用string需要引入頭檔案:#include <string>

3.5 布爾型別

代表"true(1)"或者"false(0)",表示邏輯,

  • 所占記憶體:1位元組,
  • 本質上1代表真,0代表假,
  • 使用cin輸入時,非0表示真,0表示假,0~1之間的小數視為0,

3. 運算子

包括四則運算,取余等方法,

  • 四則運算注意事項

    • 除法符號為"/"注意不要和反斜杠"\"混淆,
    • 除法運算時,兩個整數(這里指型別)相除,結果依然是整數,小數部分消除(不是四舍五入),
    • 0為除數時程式崩潰
  • 取模運算

    • 符號為"%"
    • 取模運算作用是獲取兩數相除所得余數,
    • 取模運算本質上也是除法的一種,除數不可為0,
    • 小數不可以進行取模運算
  • 遞增和遞減

    • 兩者的功能類似,都是讓變數加、減1
    • 前置遞增/遞減為先加1,后運算;后置遞增/遞減為先運算,后加1.
  • 賦值運算子

    • 包括=+=-=*=/=%=
      示例:
      int main(){
      	int a=1;
      	a=3;
      	//此時a=3;
      	a+=2;
      	//此時a=5
      	a-=3;
      	//此時a=2
      	a*=2;
      	//此時a=4
      	a/=2;
      	//此時a=2
      	a%=1;
      	//此時a=0
      	cout<<a<<endl;
      	return 0;
      }
      
  • 比較運算子

  • 包括==>=<=!=><

  • 邏輯運算子

    • 包括非!、與&&、或||
  • 三目運算子

    • 用法:運算式1 ? 運算式2 : 運算式3
    • 含義:如果運算式1成立,則回傳運算式2的運行結果,否則回傳運算式3的運行結果,
      示例:
    int main(){
    	int a=1,b=10,c=0;
    	//用法一
    	c = (a > b ? a : b);  //括號能提高三目運算的優先級防止運行出錯
    	//將a和b中值較大的賦值給c
    	
    	//用法二
    	(a > b ? a : b) = 999;
    	//把999賦給a和b中較大的變數
    	return 0;
    }
    

4. 程式流程結構

4.1 順序結構

就是從頭到尾順序執行,沒啥可記的,

4.2 選擇結構

判斷選擇,可以實作跳過或者分支,

  • if陳述句
    用法一:if(條件){滿足條件執行的代碼塊}

    • 注意:
      1. 注意不要加入多余的分號,
    • 示例:
    int main(){
    	int score;
    	cout<<"Please input your score:";
    	cin>>score;
    	if (score>=600){
    		cout<<"Good!";
    	}
    	return 0;
    }
    

    用法二:if(條件){滿足條件執行的代碼塊}else{不滿足時執行的代碼塊}

    • 注意:
      1. else為可選分支,洗掉后和用法一相同,
      2. 注意不要加入多余的分號,
    int main(){
    	int score;
    	cout<<"Please input your score:";
    	cin>>score;
    	if (score>=600){
    		cout<<"Good!";
    	}else{
    		cout<<"Bad!";
    	}
    	return 0;
    }
    

    用法三:if(條件1){滿足條件執行的代碼塊}else if(條件2){不滿足條件1但滿足條件2時執行的代碼塊}
    -,

    1. elseelse if 為可選分支.
    2. else if可并列多次使用
    int main(){
    	int score;
    	cout<<"Please input your score:";
    	cin>>score;
    	if (score>=600){
    		cout<<"Good!";
    	}
    	else if(score>=400){
    		cout<<"Bad!";
    	}
    	else{
    		cout<<"So Bad!";
    	}
    	return 0;
    }
    

    用法四:if陳述句的嵌套,我認為沒啥高級的,所以不記了,

  • switch陳述句

    • 用法:在示例中演示
    • 意義:可以輕松實作多分支
      示例:
    int main(){
    	int level;
    	cin>>level;
    	switch(level){
    	case 1:
    		cout<<"Good";
    		break;
    	case 2:
    		cout<<"Normal";
    		break;
    	case 3;
    		cout<<"Bad";
    		break;
    	default :
    		cout<<"非法輸入!請輸入1~3之間的整數,";
    		break;
    	}
    	return 0;
    }
    
    • 注意:
      1. 需要使用break跳出分支,
      2. 缺點是無法使用區間視線分支,

4.3 回圈結構

回圈執行代碼塊,

4.3.1 while陳述句

條件滿足時不斷回圈執行指定代碼塊,否則跳出回圈,

  • 用法:while(條件){條件為真時回圈執行的代碼塊}

  • 注意:

    1. 可以使用break跳出回圈,
  • 示例:

    int main(){
    	int a=0;
    	while(a<10){
    		a++;
    		cout<<a;
    	}
    	return 0;
    }
    

4.3.2 do...while陳述句

  • 用法:do{代碼塊}while(條件);

  • 注意:

    1. 基本注意事項和while相同,
  • 示例:

    int main(){
    	int a=0;
       do{
           a++;
         cout<<a<<endl;
       }
       while(a<10);
       return 0;
    }
    

4.3.3 for回圈陳述句

  • 用法:for(起始運算式;條件運算式;末尾運算式){回圈代碼塊}

  • 注意:可以使用break跳出回圈,

  • 示例:

    int main(){
        for(int i=1;i<10;i++){
            cout<<i<<endl;
        }
        return 0;
    }
    

4.4 跳轉陳述句

用于跳出或者移動當前結構中的運行位置,

4.4.1 break陳述句

  • 可以出現在switch陳述句中,用于跳出分支,
  • 可以出現在回圈陳述句中,用于跳出回圈,
  • 在位于嵌套回圈結構時,用于跳出當前所在層的回圈,

4.4.2 continue陳述句

  • 作用:在回圈陳述句中跳過余下尚未執行的陳述句,直接進入下一次回圈,

  • 示例:

    int main(){
        for (int i=1;i<=10;i++)
        {
            cout<<i<<endl;
            continue;
            cout<<"這段不被輸出\n";
        }
        return 0;
    }
    

4.4.3 goto陳述句

  • 作用:可以跳轉到任意標記的位置,

  • 示例:

    int main(){
        for(int i=1;i<=10;i++){
            cout<<"這是第一句話\n";
            cout<<"這是第二句話\n";
            goto flag;
            cout<<"這句話我們不要了\n";
            flag:
            cout<<"這是第三句話\n";
        }
        return 0;
    }
    

5. 陣列

陣列就是一個集合,里邊存放了一組相同型別的資料,

  • 特點
    1. 陣列中每個元素都是相同的資料型別,
    2. 陣列是由連續的記憶體位置組成的,

5.1 一維陣列

  • 一維陣列的定義方式:

    1. 資料型別 陣列名[陣列長度];
    2. 資料型別 陣列名 [陣列長度]={值1,值2,...,值n};
    3. 資料型別 陣列名[]={值1,值2,...,值n};
  • 訪問格式:array [0]

  • 注意事項:

    1. 訪問時下標從0開始,
  • 示例:

    int main(){
        int arr[5]={1,2,3,4};//只初始化了前四個,第五個值默認初始化為0
        for(int i=0;i<=4;i++)
        {
            cout<<arr[i]<<endl;
        }
        arr[4]=5; //這是對陣列中未初始化的第5個值賦值
        cout<<arr[0];
        return 0;
    }
    
  • 補充:

    • 陣列名的用途:
      1. 可以統計陣列或陣列中元素所占記憶體空間,(使用sizeof(陣列名/陣列名[])函式)
      2. 可以獲取陣列在記憶體中的首地址,(cout<<array;
    • 陣列名為常量,不可直接賦值,
  • 一維陣列的倒置示例:

    int main(){
        int arr [5]={1,2,3,4,5};//創建一個陣列
        int temp,a,b;
        a=0;b=sizeof(arr)/sizeof (arr[0])-1;//b為通過計算得出的陣列中元素數量減1
        while(a<b){
            temp=arr[a];
            arr[a]=arr[b];
            arr[b]=temp;
            a++;b--;
        }
        b=sizeof(arr)/sizeof (arr[0]);//為了節省記憶體,將b重置為陣列元素個數
        for(int i = 1;i<=b;i++){    //回圈b次,依次輸出陣列中每個元素的值
            cout<<arr[i-1]<<endl;
        }
        return 0;
    }
    
  • 一維陣列的順序排列示例(冒泡排序):

    int main(){
        int arr[5]={1,5,2,3,4};
        for (int i = 0;i < 5;i++){
            for(int j = 0;j<(5-i-1);j++){
                if (arr[j]>arr[j+1]){
                int temp;
                temp=arr[j];
                arr[j]=arr[j+1];
                arr[j+1]=temp;
                }
            }
        }
    
        for (int q = 1; q<=5;q++){
            cout<<arr[q-1]<<endl;
        }
        return 0;
    }
    

5.2 二維陣列

  • 定義方式:

    1. 資料型別 陣列名 [行數][列數]={{數值1,數值2...},{數值n,數值n+1...}};
    2. 資料型別 陣列名 [行數][列數];
    3. 資料型別 陣列名 [行數][列數]={資料1,資料2,資料3,資料4};
    4. 資料型別 陣列名 [][列數]={資料1,資料2,資料3,資料4};
    • 示例:

      int main(){
          //這是創建二維陣列最直觀的形式
          int arr[2][3]={
              {1,2,3},
              {4,5,6}
          };
          //使用for回圈嵌套遍歷輸出二位陣列中每個元素
          for (int i=0;i<2;i++){
              for (int j=0;j<3;j++){
                  cout<<arr[i][j]<<" ";
              }
              cout<<endl;
          }
          return 0;
      }
      
  • 二維陣列名的用途

    1. 可以統計陣列、陣列中一行或陣列中元素所占記憶體空間,(使用sizeof(陣列名/陣列名[]/陣列名[][])
    2. 可以獲取陣列在記憶體中的首地址,(cout<<array;
  • 注意:

    1. 為了程式的可讀性,我們一般使用前兩種定義方式,
    2. 第三種定義方式會自動分出行列,
    3. 第四種必須指定列數,行數會依據資料數量進行自動分配,

6. 函式

函式就是一個程式塊,可以方便的進行呼叫,能很好的減少代碼量,一個較大的程式往往分成好多模塊,每個模塊實作特定功能,

6.1函式的定義

  • 函式的定義示例:

    回傳值型別 函式名(形參)
    {
        程式代碼塊;
        retuen 回傳值運算式;
    }
    
  • 注意:

    1. 必須回傳一個正確的回傳值型別,
    2. 若不需要回傳值可以宣告void函式,

6.2 函式的呼叫

  • 語法:函式名(引數)
  • 形式引數也叫形參,是一個形式,呼叫的是使用函式時傳遞的實參
  • 形參的值在函式中發生變化不會影響到實參,

6.3 函式的宣告

  • 語法回傳值型別 函式名(形參)

  • 注意:

    • 在main函式前宣告函式防止程式運行時無法正常呼叫函式,
    • 可以有多次宣告,但是只能有一次定義
  • 示例:

    int max(int num1,int num2); //函式max的宣告
    int main(){
        cout<<max(100,101)<<endl;
        return 0;
    }
    int max(int num1,int num2){   //函式的定義
        return num1 > num2 ? num1:num2;
    } 
    //函式定義在main函式后需要在main函式前宣告,
    

6.4 函式的分檔案撰寫

為了防止單檔案形勢下代碼量過大,

  • 要素:

    1. 一個自定義的頭檔案(.h)
    2. 源檔案(.cpp)
    3. 函式的宣告寫在頭檔案中
    4. 函式的定義寫在源檔案中
  • 示例:

    • 檔案結構:

    • 代碼示例:

      • main.cpp

        #include <iostream>
        #include "max.h"
        
        using namespace std;
        
        int main(){
            cout<<max(100,101)<<endl;
            return 0;
        }
        
      • max.h

        #include <iostream>
        using namespace std;
        
        int max(int num1,int num2);
        
      • max.cpp

        #include "max.h"
        
        int max(int num1,int num2){
            return num1 > num2 ? num1:num2;
        }
        

        輸出結果:100

6.5 函式的默認值

  • 語法:回傳值型別 函式名(引數名=默認值);

  • 注意:

    • 如果某個位置開始有默認引數,那么從該位置往后都應該有默認引數,
    • 宣告和實作只能有一個設定默認引數,不允許重定義默認引數,
  • 示例:

    void print(int a=10,int b=20,int c=30){ //給所有選項都設定了默認引數
        cout<<a+b+c<<endl; 
    }
    int main()
    {
        print(1,2,3);//呼叫函式是傳遞引數
        //輸出6
        print();//呼叫函式時不傳遞引數,使用默認引數
        //輸出60
        return 0;
    }
    

6.6 函式的占位引數

  • 語法:回傳值型別 函式名 (資料型別);

  • 示例:

    void print(int = 10){ //只有資料型別,沒有變數名就是占位引數
        cout<<"Hello World!";
    }
    int main(){
        print(); //因為占位引數具有默認值,所以此處無需傳遞,否則必須傳遞一個相應型別的引數
    }
    

6.7 函式的多載

6.7.1 概述

  • 意義:函式名可以相同,提高函式復用性,

  • 條件:

    • 同一作用域下,
    • 函式名稱相同,
    • 函式引數名引數個數引數順序不同,
  • 注意:函式的回傳值不可用作函式多載的條件,

  • 示例:

    void print(){
        cout<<"print()函式被呼叫\n";
    }
    void print(int a){
        cout<<"print(int a)函式被呼叫\n";
    }
    int main()
    {
        print();//print()函式被呼叫
        print(1);//print(int a)函式被呼叫
        //其他例如引數名,引數個數,引數順序不同 同理
        return 0;
    }
    

6.7.2 函式多載的細節問題

  • 常量參考

    • 示例:

      void print(int& a){
          cout<<"print()函式被呼叫\n";//如果傳入數字1,則為int& a = 1 不合法
      }
      void print(const int& a){ //相當于const int& a = 1 合法
          cout<<"print(int a)函式被呼叫\n";
      }
      int main()
      {
          int a=1;
          print(a);
          print(1);
          return 0;
      }
      
  • 引入默認值導致的二義性

    • 示例:

      void print(int a){
          cout<<"print()函式被呼叫\n";
      }
      
      void print(int a,int b=1){
          cout<<"print(int a,int b=1)函式被呼叫\n";
      }
      int main()
      {
          print(1);//這個會報錯,因為產生了二義性
          print(1,1);//會呼叫print(int a,int b=1)函式
          return 0;
      }
      

7. 指標

可以通過指標間接訪問記憶體地址,

  • 注意:
    • 記憶體編號從0開始記錄,一般使用十六進制數字保存
    • 可以利用指標變數保存地址
    • 不管什么指標,在32位系統下占用4位元組,64位占用8位元組,

7.1 指標的定義

  • 語法:資料型別 * 指標變數名

7.2 指標的使用

  • 讓指標記錄一個地址:

    • 語法:指標變數名 = &變數名

    • 注意:

      • &為取址符,可以獲取當前記憶體地址,
  • 指標指向函式:

    • 語法:回傳值 (*指標名)(引數串列);
  • 指標的使用:

    • 通過解參考影響指標指向記憶體區域所儲存的值,

    • 示例:

    int main(){
        int a = 10;
        int * p;   //定義一個指標p
        p=&a;  //將變數a的地址賦給指標p
        //int * p = &a;   //這個方式可以將定義和賦值寫在一起
        *p=1000;  //使用解參考影響指標p指向的記憶體區域,也就是變數a
        cout<<a<<endl;
        cout<<*p<<endl;		//通過輸出展示指標所產生的影響
        return 0;
    }
    

7.3 空指標

  • 用途:給指標變數初始化,
  • 特點:空指標指向的記憶體區域無權訪問,
  • 定義一個空指標:int * p = NULL

7.4 野指標

  • 定義:指向一塊未申請的記憶體區域,
  • 特點:
    • 指向的記憶體區域通常無法讀取或修改,
    • 一旦嘗試讀取或修改,則會報錯,

7.5 const修飾指標

  • 三種方式:

    • const修飾指標:常量指標
      • 用法:const int * p = NULL
      • 作用:指標所指向的記憶體地址中的值為常量,不可更改;但是指標所指的地址可以更改,
    • const修飾常量:指標常量
      • 用法:int * const p = NULL
      • 作用:指標本身為常量,指向的記憶體地址不可更改,但是其值可以更改,
    • const同時修飾指標和常量
      • 用法:const int * const p=NULL
      • 作用:指標所指的記憶體地址不可更改,值也不可更改,

7.6 指標和陣列

用指標操作陣列,

  • 示例:

    int main(){
        int arr[5]={1,2,3,4,5};
        int * p=arr; //將指標指向陣列在記憶體中的首地址
        for (int i=0;i<5;i++){
            cout<<*p<<endl; //輸出指標p指向的值
            p++; //每次回圈將指標p向后偏移4位元組,實作指標在陣列中的遍歷
        }
        return 0;
    }
    

7.7 指標和函式

利用指標作為函式的引數可以修改實參的值,

  • 示例:

    void swap(int *p1,int *p2){  //宣告一個函式用來交換兩個變數的值,形參為兩個指標
        //這部分直接影響了指標所指向的記憶體空間,也就是main函式中a,b兩個變數的值
        int temp=*p1;
        *p1=*p2;
        *p2=temp;
    }
    int main(){
        int a=1,b=2;
        cout<<"a="<<a<<"  b="<<b<<endl;
        swap(&a,&b);  //參考函式,實參為兩個變數的地址
        cout<<"a="<<a<<"  b="<<b<<endl;
        return 0;
    }
    

# 指標、函式、陣列的搭配示例

利用冒泡回圈對陣列排序,

int bubbleSort(int * arr,int len){  //使用函式封裝冒泡回圈的演算法
    for (int i = 0;i<len;i++){
        for(int j=0;j<len-i-1;j++){
            if(arr[j]>arr[j+1]){
                int temp=arr[j];
                arr[j]=arr[j+1];
                arr[j+1]=temp;
            }
        }
    }
}
int main(){
    int arr []={1,5,3,2,7,8,3,5,2};
    int len=sizeof(arr)/sizeof (arr[0]); //獲取陣列的元素數量
    bubbleSort(arr,len);  //呼叫函式,第一個實參為arr陣列的記憶體地址
    for(int i = 0;i<len;i++){
        cout<<arr[i]<<endl;
    }
}

8. 結構體

允許用戶創建自定義資料型別,儲存不同的資料型別,

8.1 定義和使用

  • 語法:struck 結構體名 { 結構體成員串列 };

  • 創建變數方式:

    • struck 結構體名 變數名 = {成員1;成員2;...}
    • struck 結構體名 變數名
    • 定義結構體時順便創建變數
  • 使用變數的屬性

    • 格式:變數名.屬性
  • 示例:

    struct student //定義結構體(全域),struct關鍵字不可省略
    {
        string name;
        int age;
        int score;
    }s3; //此處s3為使用方法三創建的結構體變數
    
    int main(){
        //方法一
        struct student s1={"Maicss",19 , 650 } ;      //struct關鍵字可以省略
        cout<<"name:"<<s1.name<<"\tage:"<<s1.age<<"\tscore:"<<s1.score<<endl;
    
        //方法二
        struct student s2;   //struct關鍵字可以省略
        s2.name="qian";
        s2.age=18;
        s2.score=600;
        cout<<"name:"<<s2.name<<"\tage:"<<s2.age<<"\tscore:"<<s2.score<<endl;
    
        //方法三
        //創建結構體變數在定義結構體之后
        s3.name="son";
        s3.age=1;
        s3.score=700;
        cout<<"name:"<<s3.name<<"\tage:"<<s3.age<<"\tscore:"<<s3.score<<endl;
        return 0;
    }
    

8.2 結構體陣列

  • 作用:將自定義的結構體放入陣列中方便維護,

  • 語法:struck 結構體名 陣列名[成員數] = { { } , { } , { } }

  • 示例:

    struct student {  //定義一個結構體
        string name;
        int age;
        int score;
    };
    int main()
    {
        //創建一個結構體陣列
        struct student stuarr[3]{
        {"Maicss",19,100},
        {"Max",20,99},
        {"Pig",10,30}
        };
        
        //給結構體陣列中第三個成員的score修改為98
        stuarr[2].score =98;
    
        //遍歷輸出結構體陣列所有成員屬性
        for (int i=0 ;i<3;i++){
            cout<<"name:"<<stuarr[i].name<<"\tage:"<<stuarr[i].age<<"\tscore:"<<stuarr[i].score<<endl;
        }
        return 0;
    }
    

8.3 結構體指標

  • 作用:通過指標訪問結構體中的成員,

  • 利用運算子->可以通過結構體指標訪問結構體屬性,

  • 示例:

    struct student {  //定義一個結構體
        string name;
        int age;
        int score;
    };
    int main()
    {
        //創建一個結構體陣列
        student s{"Maicss",19,100};
    
        //創建一個結構體指標
        student * p = &s;
    
       //通過指標讀取結構體變數屬性
        cout<<"name:"<<p->name<<"\tage:"<<p->age<<"\tscore:"<<p->score<<endl;
        return 0;
    }
    

8.4 結構體嵌套

  • 作用:讓一個結構體成為另一個結構體的成員,

  • 示例:

    struct student {  //定義一個結構體
        string name;
        int age;
        int score;
    };
    struct teacher { //定義另一個結構體
        string name;
        int id;
        int age;
        student std;
    };
    int main()
    {
        //創建一個結構體變數
        student std1 {"max",18,100};
        //創建另一個結構體變數
        teacher thr1 {"maicss",197835,23,std1};
    
        //創建一個結構體指標
        teacher * p = &thr1;
    
        //修改老師maicss的學生max的年齡為19
        thr1.std.age=19;
        
       //通過指標讀取結構體變數屬性
        cout<<"name:"<<p->name
               <<"\tID:"<<p->id
               <<"\tage:"<<p->age
               <<"\tstudent:"<<p->stds.name  //讀取嵌套在teacher結構體中的student的屬性
            	 <<"\tstudentAge:"<<p->stds.age  //查看更改后的學生年齡
               <<endl;
        return 0;
    }
    

8.5 將結構體作為函式引數傳遞

  • 值傳遞

    • 直接在函式引數中傳遞結構體變數,
    • 特點:在函式內對引數值(結構體屬性)的修改不會影響實參,
  • 指標傳遞

    • 在函式引數中傳遞結構體指標,
    • 特點:對值(結構體屬性)的修改會影響到實參,
  • const 保護

    • 用const修飾指標,防止在函式中無意影響到函式實參,
  • 示例:

    struct student {  //定義一個結構體
        string name;
        int age;
        int score;
    };
    void print1(student a){ //結構體在函式引數中通過值傳遞
        cout<<"name:"<<a.name<<" age:"<<a.age<<" score:"<<a.score<<endl;
    }
    void print2(student * p){ //結構體在函式引數中通過指標傳遞
        p->age=100; //通過指標修改結構體屬性的值可以影響到實參
        cout<<"name:"<<p->name<<" age:"<<p->age<<" score:"<<p->score<<endl;
    }
    int main()
    {
        //創建一個結構體變數
        student std1 {"max",18,100};
    
        //創建一個結構體指標
        student * p =&std1;
    
        //呼叫函式進行輸出
        print1(std1); //值傳遞
        print2(&std1);//指標傳遞
        cout<<"name:"<<std1.name<<" age:"<<std1.age<<" score:"<<std1.score<<endl; //驗證print2函式的修改
        return 0;
    }
    
    

9. 聯系人管理系統實體

一個完整的聯系人管理系統

10 程式的記憶體模型

10.1 記憶體磁區模型

  • 代碼區:存放函式體的二進制代碼,由作業系統進行管理,
  • 全域區:存放全域變數和靜態變數以及常量,
  • 堆疊區:由編譯器自動分配和釋放,存放函式的引數值,區域變數等,
  • 堆區:由程式員分配和釋放,若程式員不釋放,程式結束時由系統回收,

10.2 磁區意義

  • 不同區域存放的資料賦予不同的生命周期,更強大的靈活的編程,

10.3 程式運行前

程式編譯后,生成exe可執行程式,未執行該程式前分為兩個區域,

  • 代碼區:
    • 存放CPU執行的機器指令,
    • 代碼區是共享的,共享的目的是對于頻繁被執行的程式,只需要在記憶體中有一份代碼即可,
  • 全域區:
    • 全域變數和靜態變數存放在此,
    • 全域區也包含了常量區,字串常量和其他非區域常量也存放在此,
    • 該區域的資料在程式結束后由作業系統釋放,

10.4 程式運行后

  • 堆疊區

    • 由編譯器自動分配和釋放,存放函式的引數值,區域變數等,

    • 注意:不要回傳堆疊區的地址,堆疊區開辟的資料由編譯器自動釋放,

    • 堆疊區的資料在函式執行完后自動釋放,

    • 示例:

      int* integer(){
          int a=1;
          return &a; //回傳區域變數a的記憶體地址
      }
      int main()
      {
          int * p=integer(); //將指標p指向區域變數a的地址
          cout <<*p<< endl;  //第一次解參考p
          //這里之所以會輸出a是因為編譯器為程式保留了一次記憶體
          cout <<*p<< endl;//第二次解參考p
          //編譯器不再為程式保留,所以不再是原來的數值
          return 0;
      }
      
  • 堆區

    • 由程式員分配和釋放,若程式員不釋放,程式結束時由系統回收,

    • 使用new在堆區開辟記憶體,

      • 語法:new 資料型別
      • new創建的資料會回傳該資料型別對應的指標,
    • 使用delect釋放記憶體,

      • 語法:delect 指標
      • 釋放后不能繼續訪問,
  • 為了防止出現野指標,在堆區記憶體空間被釋放時要將指向該記憶體的指標指向NULL

    • 示例:
      int main()
      {
          int * p = new int(10); //使用new開辟一塊記憶體儲存整數10并將地址給指標p
          int * p2 = new int[10]; //創建一個陣列
          cout<<*p<<endl;//解參考指標p結果為10
          //賦值部分省略
      delete p;//釋放記憶體
          delete[] p2;//釋放陣列
      }
      

11. C++中的參考

參考的本質是指標常量,

11.1 參考的基本使用

  • 作用:給變數起別名,

  • 語法:&別名=原名

  • 注意事項:

    • 參考必須要初始化,
    • 參考一旦初始化就不可以更改,

11.2 參考作函式引數

  • 作用:就像指標一樣,可以影響到實參,

  • 示例:

    //創建一個交換函式
    void swap(int &a,int &b){ //使用參考傳遞引數
      int temp;
        temp=a;
        a=b;
        b=temp;
    }
    int main()
    {
        int a,b;
        a=0;b=1;
        swap(a,b);
        cout<<a<<"\n"<<b<<endl;
    }
    

    11.2 參考作函式的回傳值

    • 作用:可以讓函式的呼叫作為左值,

    • 示例:

      //創建一個函式
      int& num(){
          static int a=10; //創建靜態變數,儲存在全域區中
          return a;
      }
      int main()
      {
          int& ref=num(); // 給num函式中的a搞一個參考
          num()=1000;
          cout<<ref<<endl; //驗證將函式呼叫作為左值是否有效
          return 0;
      }
      
      

11.3 常量參考

  • 作用:可以用來修飾形參,防止實參被影響,

  • 示例:

    void change(const int& a){ //使用const修飾形參
        //此處不可修改a的值
        cout<<a;
    }
    int main()
    {
        int a=1;
        change(a);
        return 0;
    }
    

12. 類和物件

C++面向物件的三大特性:封裝、繼承、多型

萬物皆物件、物件上有其屬性和行為,

12.1 封裝

12.1.1 封裝的意義

  • 封裝是c++面向物件三大特征之一

  • 封裝的意義:

    • 將屬性和行為作為一個整體來表現實物,
    • 將屬性和行為加以權限控制,
  • 示例:

    #include <iostream>
    using namespace std;
    
    #define Pi 3.1415926 //定義一個宏常量Pi
    
    class circle{ //創建一個circle類
    public:  //定義公有部分
        int r;  //定義一個半徑屬性r
        double circle_C(){  //定義一個成員函式,計算周長
            return 2*r*Pi;
        }
        void set_r(double set_r){
            r=set_r;
        }
        
    };
    int main()
    {
        circle yuan; //通過circle類實體化一個物件
        yuan.r=10; //給物件的公有屬性r賦值
        cout << yuan.circle_C() << endl; //通過成員函式輸出周長
        yuan.set_r(5);
        cout << yuan.circle_C() << endl; //通過成員函式輸出周長驗證修改
        return 0;
    }
    

12.1.2 訪問權限控制

  • 總共有三個權限

    名稱 意義 特點
    public 公共權限 類內和類外都可以訪問
    protected 保護權限 類內可以訪問,類外不可以訪問,子可以訪問父的保護內容
    private 私有權限 類內可以訪問,類外不可以訪問,子不可訪問父的私有內容
  • classstruct的區別:

    • class默認是私有權限,struct默認是公有權限,

12.1.3 成員屬性私有化

  • 優點:

    • 對成員屬性私有化可以自己控制讀寫權限,
    • 對于讀寫權限,可以檢測資料的有效性,
  • 示例:

    #include <iostream>
    using namespace std;
    
    class human{
    public:
        void setName(string set_name){ //定義一個用來設定姓名的成員函式
            name=set_name;
        }
        string getName(){  //定義一個獲取姓名的成員函式
            return name;
        }
        void setAge(int set_age){  //定義一個用來設定年齡的成員函式
            if (set_age<0 || set_age>150){  //使用if陳述句判斷所給值是否合法
                cout<<"非法資料!";
            }else{
                age=set_age;
            }
        }
        int getAge(){  //定義一個用來獲取年齡的成員函式
            return age;
        }
        void setLover(string set_lover){ //定義一個用來設定愛人的成員函式
            lover=set_lover;
        }
    private:  //私有屬性的定義
        string name;
        int age;
        string lover;
    };
    int main()
    {
        human maicss; //實體化一個物件
        //使用成員函式設定相關屬性
        maicss.setName("Maicss");
        maicss.setAge(18);
        maicss.setLover("qian");
        //使用成員函式獲取相關私有函式的值在
        cout<<"name:"<<maicss.getName()<<" age:"<<maicss.getAge()<<endl;
        return 0;
    }
    

12.2 物件的初始化和清理

12.2.1 建構式和解構式

  • 物件的初始化清理是兩個重要的問題,

    • 一個物件或者變數沒有初始化狀態,其使用后果是未知的,
    • 使用完一個物件或者變數,沒有及時的清理,也會造成一定的安全問題,
  • 注意:

    • 建構式和解構式可以解決上述問題,由編譯器自動呼叫,完成物件的初始化和清理作業,物件的初始化和清理是必須要做的事情,因此不必要提供構造和解構式,編輯器會提供,但是編譯器提供的建構式和解構式是空實作
    • 一個空物件所占用的記憶體為1位元組,因為物件必須要有一個首地址,
  • 作用:

    • 建構式:主要用在創建物件時給物件的成員屬性賦值,由編譯器自動呼叫,
    • 解構式:主要用于物件銷毀前的自動呼叫,負責清理作業,
  • 語法:

    • 建構式:類名(){}

      1. 建構式,沒有回傳值,也不用寫void,
      2. 函式名稱和類名相同,
      3. 建構式可以有引數,因此可以發生多載,
      4. 程式在呼叫物件的時候自動呼叫建構式,并且只會呼叫一次,無需手動呼叫,
    • 解構式:~類名(){}

      1. 解構式,沒有回傳值,也不用寫void,
      2. 函式名稱和類名相同,在名稱前加上~
      3. 解構式不可以有引數,因此無法發生多載,
      4. 程式在物件銷毀前自動呼叫解構式,并且只會呼叫一次,無需手動呼叫,
    1. 手動釋放在堆區申請的空間要用delete陳述句,
  • 示例:

    class human{
    public: //為了保證建構式和解構式能被全域呼叫,所以需要設定為public權限
        human(){ //創建一個建構式
            cout<<"human的建構式被呼叫\n";
        }
        ~human(){ //創建一個解構式
            cout<<"human的解構式被呼叫\n";
        }
    
    };
    void hum(){ //函式中實體化一個物件,函式運行結束后物件被銷毀
        human m;
    }
    int main()
    {
        human maicss;//創建物件同時運行建構式
        hum(); //呼叫hum函式來創建物件,同時運行建構式
        //hum函式運行結束時物件m被銷毀,同時運行解構式
        return 0;//mian函式回傳0,程式結束前會運行解構式
    }
    

12.2.2 建構式的分類以及呼叫

  • 兩種分類方式:

    • 按引數分為:有參構造和無參構造
    • 按型別分為:普通構造和拷貝構造
      • 拷貝建構式是用來將一個物件的所有屬性拷貝到新物件中,
  • 三種呼叫方式:

    • 括號法
    • 顯示法
    • 隱式轉換法
  • 注意事項:

    • 使用默認建構式時,不要用(),否則會被認為是函式定義,
    • 不要利用拷貝建構式初始化匿名物件,編譯器會認為是引數物件的宣告,
  • 示例(有點長):

    class human{
    public:
        human(){
            cout<<"human的無參(默認)建構式被呼叫\n";
        }
        human(int a){
            age=a;
            cout<<"human的有參建構式被呼叫\n";
        }
        human(const human &p){ //const的意義是不允許在建構式中修改傳入物件的屬性,只讀,
            age=p.age;
            cout<<"human的拷貝建構式被呼叫\n";
        }
        ~human(){
            cout<<"human的解構式被呼叫\n";
        }
        int age;
    
    };
    void hum1(){  //括號法呼叫
        human m1;   //無參構造
        human m2(10);  //有參構造
        human m3(m1);  //拷貝構造
    }
    void hum2(){  //顯示法
        human m1;  //無參構造
        human m2=human(10);  //有參構造
        human m3=human(m1);  //拷貝構造
    }
    void hum3(){  //隱式轉換法
        human m1;  //無參構造
        human m2=10;  //有參構造
        human m3=m2;  //拷貝構造
    }
    int main()
    {
        hum1();
        hum2();
        hum3();
        return 0;
    }
    

12.2.3 拷貝建構式的呼叫時機

C++中拷貝建構式的呼叫時機通常由三種情況:

  • 使用一個創建完畢的物件來初始化一個新的物件,
  • 以值傳遞的方式給函式的引數傳值
  • 以值方式回傳區域物件

12.2.4 建構式的呼叫規則

默認情況下,C++編譯器至少給一個類添加三個函式:

  1. 默認建構式(無參,函式體為空)
  2. 默認解構式(無參,函式體為空)
  3. 默認拷貝建構式,對其屬性值進行拷貝,

建構式呼叫規則:

  • 如果用戶自定義有建構式,編譯器不再提供默認無參建構式,但是會提供默認拷貝構造,
  • 如果用戶定義拷貝建構式,編譯器不再提供其他建構式,
  • 總結:寫了有參必須寫無參,寫了拷貝就得有參無參都寫上,

12.2.5 深拷貝與淺拷貝

  • 定義:

    • 淺拷貝:簡單的賦值操作,如果是用編譯器提供的拷貝函式會利用淺拷貝,
    • 深拷貝:在堆區重新申請一塊記憶體空間,進行拷貝操作,
  • 注意:

    • 淺拷貝可能導致堆區記憶體重復釋放,
  • 示例(淺拷貝):

    class person{
    public:
        person(){
            cout<<"無參建構式被呼叫\n";
        }
        person(int age,int height){
            m_height=new int (height); //在堆區申請一塊記憶體區域用來存放height
            m_age=age;
            cout<<"有參建構式被呼叫\n";
        }
        ~person(){
            if(m_height!=NULL){
                 delete m_height; //釋放m_height指向的記憶體區域
            //解構式會被執行兩次,因為兩個物件在man()函式結束后被銷毀,但是由于淺拷貝將指標拷貝給第二個物件,因此兩個物件的m_height指標指向了堆區的同一塊記憶體區域,這塊記憶體區域釋放兩次,會報錯,
            
                 m_height=NULL;  //將指標指向NULL,防止野指標的出現,
            }
            cout<<"解構式被呼叫\n";
        }
    private:
        int m_age;
        int * m_height; //創建一個int指標指向有參構造申請的記憶體區域
    };
    void man(){
        person one(16,160);
        person two(one);//淺拷貝
    }
    int main()
    {
        man();
        cout << "Hello World!" << endl;
        return 0;
    }
    
  • 示例(深拷貝):

    class person{
    public:
        person(){
            cout<<"無參建構式被呼叫\n";
        }
        person(int age,int height){
            m_height=new int (height); //在堆區申請一塊記憶體區域用來存放height
            m_age=age;
            cout<<"有參建構式被呼叫\n";
        }
        person(const person &p){
            m_age=p.m_age;
            m_height=new int (*p.m_height);//在堆區重新申請一塊記憶體實作深拷貝
        }
        ~person(){
            if(m_height!=NULL){
                 delete m_height; //釋放m_height指向的記憶體,此時不會出現多次釋放同一記憶體空間的問題
                 m_height=NULL;  //將指標指向NULL,防止野指標的出現,
            }
            cout<<"解構式被呼叫\n";
        }
    
        int m_age;
        int * m_height; //創建一個int指標指向有參構造申請的記憶體區域
    };
    void man(){
        person one(16,160);
        person two(one);//由于定義了拷貝函式,所以此處會通過定義實作深拷貝
        cout<<one.m_age<<" "<<*one.m_height<<endl;
        cout<<two.m_age<<" "<<*two.m_height<<endl;
    }
    int main()
    {
        man();
        cout << "Hello World!" << endl;
        return 0;
    }
    

12.2.6 初始化串列

  • 作用:初始化串列語法可以用來初始化物件屬性,

  • 語法:建構式():屬性1(值1),屬性2(值2), ...

  • 示例:

    class person{
    public:
        person(int a,int b):age(a),height(b){}
        int age;
        int height;
    };
    void man(){
        person one(18,180);
        cout<<"age:"<<one.age<<" height:"<<one.height<<endl;
    }
    int main()
    {
        man();
        return 0;
    }
    

12.2.7 類物件作為類成員

  • 定義:一個類宣告的物件成為另一個類的屬性成員,

  • 例如:

    class A{}
    class B{
        A a;
    }
    
  • 注意:

    • 構造時先構造作為屬性成員的物件(A)再構造物件本身(B),
    • 析構時先析構物件本身(B)再析構各個屬性成員(A),

12.2.8 靜態成員

靜態成員可以看作屬于類的作用域,被所有物件公用,

靜態成員變數和靜態成員函式都有權限控制,

  • 靜態成員變數

    • 作用:所有成員公用一個成員變數,

    • 語法:static 資料型別 變數名

    • 注意:

      • 靜態成員變數要在類內宣告,類外初始化,
      • 在編譯階段會分配記憶體
    • 示例:

      class human{
      public:
          static int age;//在類內的宣告
      };
      int human::age=100;//在類外的初始化
      int main()
      {
          human a; 
          cout<<a.age<<endl;
          human b;
          b.age=18;//使用物件b給靜態變數重新賦值
          //也可以通過類名操作成員變數
          human::age=18;
          cout<<a.age<<endl;//此時物件a的age值也會隨b變成18
          return 0;
      }
      
  • 靜態成員函式

    • 作用:所有成員公用一個成員函式,屬于類的作用域,

    • 語法:static 函式回傳值型別 函式名();

    • 注意:靜態成員函式屬于類的作用域,只能操作靜態成員變數,

    • 示例:

      class human{
      public:
          static void func(){
              age=1;
          }
          static int age;//在類內的宣告
      };
      int human::age=100;//在類外的初始化
      int main()
      {
          human a;
          cout<<a.age<<endl;
          human b;
          //訪問靜態成員函式,下面兩種方式效果完全相同
          b.func();//通過物件訪問
          human::func();//通過類的作用域訪問
          
          cout<<a.age<<endl;
          return 0;
      }
      

12.3 C++物件模型和this指標

12.3.1 成員變數和成員函式分開儲存

  • 非靜態成員變數屬于類的物件
  • 靜態成員變數不屬于類的物件,
  • 非靜態成員函式不屬于類的物件,
  • 靜態成員函式不屬于類的物件,

12.3.2 this指標概念

  • 作用:this指標指向被呼叫的成員函式所屬的物件,

  • 特點:

    • 隱含在每一個非靜態成員函式內的一種特殊指標,
    • this指標不需要定義,直接用即可,
  • 使用場景:

    • 當形參名和成員變數名相同時,可以用this指標區分,
    • 在類的非靜態成員函式中回傳物件本身,可以用return *this
  • 示例:

    class human{
    public:
        void c_age(int age){
            this->age=age;//用this指標表示成員變數
        }
        human& addage(human &p){ //函式回傳值要用參考的方式回傳,否則會創建新物件
            this->age+=p.age;
            return *this;
        }
        int age;
    };
    void func(){
        human maicss;
        human p1;
        p1.age=10;
        maicss.c_age(18);
        maicss.addage(p1).addage(p1).addage(p1); //鏈式編程思想
        cout<<"maicss的年齡是:"<<maicss.age<<endl;
    }
    int main()
    {
        func();
        return 0;
    }
    
    

12.3.3 空指標呼叫成員函式

空指標可以呼叫成員,但是為了防止崩潰,要避免訪問成員變數,

class human{
public:
    void printname(){
       cout<<"name is maicss\n";
    }
    void printage(){
        if (this==NULL){//防止程式崩潰進行的保險措施
            return ;
        }
        cout<<"age is "<<this->age<<endl;
    }
    int age;
};
void func(){
    human * maicss=NULL;
    maicss->printname();//使用空指標訪問成員函式
    maicss->printage();//使用空指標在成員函式中訪問成員變數

}
int main()
{
    func();
    return 0;
}

12.3.4 const修飾成員函式

常函式

  • 成員函式后加const,我們稱這個函式為常函式,
  • 常函式內不可以修改成員屬性,
  • 成員屬性加關鍵字mutable后,在常函式中依然可以修改,

常物件

  • 宣告物件前加const稱該物件為常物件,
  • 常物件只能呼叫常函式,

示例

class human{
public:
    human(){}//新建一個無參建構式,為了創建常物件
    void func() const{
       //age=18; //由于成員函式末尾加了const,所以函式體內不允許修改成員變數
        height=180; //由于成員變數前加了mutable關鍵字,所以該變數可以在函式中修改
    }
    void func2(){}
    int age;
    mutable int height;
};
int main()
{
    
    human maicss;
    const human maicss2;
    //maicss2.func2(); //由于是常物件,所以只能呼叫常函式,也只能修改帶有mutable關鍵字的成員變數
    maicss.func();
    return 0;
}

12.4 友元

  • 作用:讓一個函式或類,訪問另一個類中的私有成員,

  • 關鍵字:friend

  • 三種實作方式:

    • 全域函式作友元
    • 類作友元
    • 成員函式作友元
  • 示例:

    class room; //宣告類
    class goodgay2{
    public:
        goodgay2();
        void visit();
        room * m_room; //創建一個指標
    };
    class goodgay{
    public:
        goodgay(); //宣告建構式
        void visit(); //宣告成員函式用于訪問room的私有成員
        room * m_room; //創建一個指標
    };
    
    class room{
        friend void text();  //將全域函式作為友元
        friend class goodgay;  //將另一個類作為友元
        friend void goodgay2::visit();
    public:
        string sittingroom="客廳";
    private:
        string bedroom="臥室";
    };
    
    goodgay::goodgay(){  //類外定義建構式
        m_room=new room; //在建構式中于堆區創建一個物件
    }
    goodgay2::goodgay2(){
        m_room=new room;
    }
    void goodgay::visit(){   //類外定義成員函式
        cout<<"b在訪問:"<<m_room->bedroom<<endl;
        cout<<"b在訪問:"<<m_room->sittingroom<<endl;//訪問room的私有成員
    };
    void goodgay2::visit(){
        cout<<"c在訪問:"<<m_room->bedroom<<endl;//訪問room的私有成員
    }
    void text(){
        room a;
        cout<<"a訪問了:"<<a.bedroom<<endl;
        goodgay b;
        b.visit(); //通過訪問visit成員函式訪問room的私有成員
        goodgay2 c;
        c.visit();
    }
    
    int main()
    {
        text();
        return 0;
    }
    

12.5 運算子的多載

12.5.1 加號運算子的多載

  • 方式:

    • 使用成員函式多載
    • 使用全域函式多載
  • 示例:

    //使用成員函式多載
    class Person{
    public:
        int age;
        int height;
    public:
        Person operator+(Person &p); //使用成員函式對加號的多載
    };
    Person Person::operator+(Person &p){//定義多載函式
        Person temp;
        temp.age=this->age+p.age;
        temp.height=this->height+p.height;
        return temp;
    }
    int main(){
        Person p1;
        Person p2;
        p1.age=10;
        p2.age=18;
        p1.height=159;
        p2.height=180;
        Person p3=p1+p2;
        cout<<"P3的年齡為:"<<p3.age<<" P3的身高為:"<<p3.height<<endl;
        return 0;
    }
    
    //使用全域函式多載
    class Person{
    public:
        int age;
        int height;
    };
    Person operator+(Person &p1,Person &p2){ //使用成員函式對加號的多載
        Person temp;
        temp.age=p1.age+p2.age;
        temp.height=p1.height+p2.height; 
        return temp;
    }
    int main(){
        Person p1;
        Person p2;
        p1.age=10;
        p2.age=18;
        p1.height=159;
        p2.height=180;
        Person p3=p1+p2;
        cout<<"P3的年齡為:"<<p3.age<<" P3的身高為:"<<p3.height<<endl;
        return 0;
    }
    

12.6 繼承

繼承使面向物件三大特性之一

定義某些了類時,下一級別的成員擁有上一級別的共性,還有自己的特性,

使用繼承可以盡量減少代碼

  • 用法:class A : public B;

  • 說明:上述A為子類(派生類),B為父類(基類),

  • 示例:

    class base{ //創建一個基類
    public:
        int age;
        string name;
    };
    class human : public base{ //創建一個以base為基類的派生類
    public:
        int score;
    };
    class dog : public base{//另一個以base為基類的派生類
    public:
        human master;
    };
    void test1(){ //對派生類中屬性的訪問示例
        human maicss;
        dog dazhuang;
        dazhuang.age=4;
        maicss.age=20;
        dazhuang.name="DAZ";
        maicss.name="Maicss";
        dazhuang.master=maicss;
        maicss.score=100;
    }
    int main()
    {
        void test1();
        return 0;
    }
    
  • 注意:

    • 經過測驗,若定義基類時關鍵字改為private,那么所繼承的所有屬性全為私有屬性;若為public,則正常繼承,(這是依我自己理解的)
    • 被定義為protected的成員變數為保護權限,此時子類可以訪問這種成員變數,但是如果是private則無法訪問,這也是兩者的唯一區別,

13. 檔案操作

使用檔案操作需要包含頭檔案<fstream>

檔案型別分為兩種:

  1. 以ASCII碼形式儲存的文本資料,
  2. 二進制檔案形式儲存的,用戶一般讀不懂,

操作檔案三大類:

  1. ofstream 寫檔案
  2. ifstream 讀檔案
  3. fstream 讀寫檔案

13.1文本檔案

13.1.1寫檔案的基本操作

寫檔案的基本步驟:

//1.包含頭檔案
#include <fstream>
//2.創建流物件
ofstream ofs;
//3.打開檔案
ofs.open("檔案路徑",打開方式);
//4.寫資料
ofs<<"文本檔案";
//5.關閉檔案
ofs.close();
打開方式 解釋
ios::in 為讀檔案而打開檔案
ios::out 為寫檔案而打開檔案
ios::ate 初始位置:檔案尾
ios::app 追加方式寫檔案
ios::trunc 如果檔案存在,先洗掉再創建
ios::binary 二進制方式

檔案打開方式配合使用需要|符號

示例:

#include <iostream>
#include <fstream> //包含檔案流頭檔案

using namespace std;

int main()
{
    std::ofstream ofs; //創建一個ofstream類物件,實作寫檔案
    ofs.open("text.txt",ios::out); //打開檔案
    ofs<<"你好世界"; //寫到檔案
    return 0;
}

13.1.2讀檔案的基本操作

寫檔案的基本步驟:

//包含頭檔案
#include <fstream>
//創建流物件
std::ifstream ifs;
//打開檔案
ifs.open("檔案路徑",打開方式);
if (!ifs.is_open()){
    cout<<"檔案打開失敗"<<endl;
    return ;
}
//讀入檔案
    //第一種方式
    char text1[1024]={0};
    while (ifs>>text1) {
        cout<<text1<<endl;
    }
    //第二種方式
    char text2[1024]={0};
    while (ifs.getline(text2,sizeof(text2))){
        cout<<text2<<endl;
    }
    //第三種方式
    string text3;
    while (getline(ifs,text3)) {
        cout<<text3<<endl;
    }
    //第四種方式
    char text4;
    while ((text4=ifs.get())!=EOF){
        cout<<text4;
    }
//關閉檔案
close

14. C++中的STL

STL是為了提高軟體代碼的復用性而產生的一種標準模板庫

14.1 STL的基本概念

  • STL(Standard Template Library,標準模板庫)
  • STL從廣義上分為:容器(container)、演算法(algorithm)、迭代器(iterator)
  • 容器演算法之間通過迭代器無縫連接,
  • STL幾乎所有代碼都采用了模板類或模板函式,

14.2 vector容器的基本使用

  • 容器:vector

  • 演算法:for_each

  • 迭代器:vector<int>::iterator

  • 示例:

    #include <iostream>
    using namespace std;
    #include <vector>//使用容器必須引入頭檔案
    void print(int i) {//第三種遍歷方法要用到
    	cout << i << endl;
    }
    int main() {
    	vector<int> v;
    	v.push_back(1);//使用尾插添加元素
    	v.push_back(2);
    	v.push_back(3);
    	v.push_back(4);
        
        //遍歷容器的第一種方法
    	vector<int>::iterator head = v.begin();//begin()會回傳指向容器中第一個元素的指標
    	vector<int>::iterator tail = v.end();//end()會回傳指向容器中最后一個元素的下一個位置的指標
    	while (head != tail)
    	{
    		cout << *head << endl;
    		head++;
    	}
        //遍歷容器的第二種方法(是第一種方式的簡化)
        for (vector<int>::iterator h = v.begin(); h != v.end(); h++) {
    		cout << *h << endl;
    	}
        //遍歷容器的第三種方法(使用標準演算法庫中的for_each)
        for_each(v.begin(),v.end(),print);
        
    	getchar();
    	return 0;
    }
    

14.3 string容器的基操

  • stringchar *的區別
    • 本質上兩者區別不大,前者是后者的封裝,可以管理字串,
  • 建構式
    • string(); 無參構造,創建一個空的字串,
    • string(const char* s); 使用字串s進行初始化,
    • string(const string& str);使用字串str初始化,
    • string(int n,char c);使用n個字符c初始化,

# 其他內容

亂數生成

  • rand()函式
    • 用法:rand()%10可以生成0~9的亂數,
    • 置亂數種子:srand((unsigned int)time(NULL))(需要#include <ctime>)

記憶體

  • 獲取記憶體地址

    • 陣列的首地址可以直接使用陣列的名字,
    • 或者使用取址符“&”,
  • 注意:

    • 0~255之間的記憶體是無法訪問的,

靜態變數

  • 在普通變數前加static為靜態變數,
  • 靜態變數儲存在記憶體的全域區中,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/272419.html

標籤:C++

上一篇:JAVA RSA無填充加密求助,通過模和指數進行加密

下一篇:Flutter Widget中的State

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more