模板手機網站建設公司濟南百度推廣優(yōu)化
筆記
上一筆記接續(xù)(練習2的答案)
練習:要求在堆區(qū)連續(xù)申請5個int的大小空間用于存儲5名學生的成績,分別完成空間的申請、成績的錄入、升序排序、成績輸出函數以及空間釋放函數,并在主程序中完成測試
要求使用new和delete完成
頭文件
#ifndef TEST_H
#define TEST_H#include<iostream>
using namespace std;//聲明申請空間函數
int * apply(int size);//聲明錄入成績的函數
void input_score(int *arr, int size);//聲明輸出成績的函數
void output_score(int *arr, int size);//聲明排序函數
void sort_score(int *arr, int size);//聲明釋放空間函數
void free_space(int *arr);#endif // TEST_H
源文件
#include"test.h"
#include<algorithm>//申請空間函數的定義
int *apply(int size)
{//在堆區(qū)申請空間int *arr = new int[size];if(NULL==arr){cout<<"空間申請失敗"<<endl;return NULL;}cout<<"空間申請成功"<<endl;return arr;
}//錄入成績的函數
void input_score(int *arr, int size)
{for(int i=0; i<size; i++){cout<<"請輸入第"<<i+1<<"個學生的成績:";cin >> arr[i];}cout<<"錄入完畢"<<endl;
}//輸出成績的函數
void output_score(int *arr, int size)
{cout<<"學生成績分別是:";for(int i=0; i<size; i++){cout<<arr[i]<<"\t";}cout<<endl;}//排序函數
void sort_score(int *arr, int size)
{for(int i=1; i<size; i++){for(int j=0; j<size-i; j++){if(arr[j]>arr[j+1]){int temp = arr[j];arr[j] = arr[j+1];arr[j+1] = temp;}}}cout<<"排序成功"<<endl;
}//釋放空間函數的定義
void free_space(int *arr)
{if(NULL!=arr){delete []arr; //釋放堆區(qū)內存空間arr = NULL;}
}
主程序(驗證代碼正確性)
#include <iostream>
#include"test.h"using namespace std;int main()
{//調用申請int *arr = apply(5);//錄入學生信息input_score(arr, 5);//輸出學生信息output_score(arr, 5);//排序sort_score(arr, 5);//輸出排序后的結果output_score(arr, 5);//銷毀空間free_space(arr);arr = NULL;cout << "Hello World!" << endl;return 0;
}
7.3 malloc\free與new\delete的區(qū)別
共同點:都能夠手動完成堆區(qū)空間的申請和釋放,返回的都是堆區(qū)空間的地址
差異點:
1.????????malloc\fjree是庫函數的調用? ? ? ? ? ? new\delete是關鍵字
2.? ? ? ? malloc申請空間時無法對其進行初始化,new申請空間時可以對其進行初始化
3.? ? ? ? malloc申請空間以字節(jié)為單位,new申請空間時以數據類型為單位
4.? ? ? ? malloc申請的空間默認返回結果是void * 類型,使用時需要強制轉換,
? ? ? ? ? ?new申請空間默認為申請時的數據類型來返回對應的類型指針,(正常使用時)無需強制轉換5.? ? ? ? ?malloc申請空間時,不區(qū)分單個空間和連續(xù)空間,而new和delete區(qū)分
6.? ? ? ? new申請空間時,會自動調用類的構造函數,而malloc不會
7.? ? ? ? delete釋放空間時,會自動調用類的解構函數,而free不會
八、引用(reference)
8.1 引用的引入
1> 在C語言中,向函數中傳遞數據的方式有兩種,分別是值傳遞和地址傳遞。當實參傳遞的是變量的地址時,可能是值傳遞也可能是地址傳遞;當實參傳遞的是普通變量時,一定是值傳遞。
2> C++中引入的“引用”的概念,不用區(qū)分值和地址傳遞了,可以直接傳遞該變量本身。當進行引用傳遞時,無需在被調函數中創(chuàng)建新的變量或者指針作為載體。被調函數無需為實參分配任何空間。
8.2 引用的概念
1> 引用相當于給變量起個“別名”
2> 一個變量的引用和引用的目標使用的是同一個內存空間,就像 宋江和及時雨的關系
3> 引用的分類:左值引用和右值引用
4> 左值引用的定義格式:數據類型 &引用名 = 引用目標;
5> 右值引用的定義格式:數據類型 &&引用名 = 引用目標;
6> 總結 & 的使用方式
????????1、兩個&作為雙目運算符,表示邏輯與
????????2、一個&作為雙目運算符,表示按位與
????????3、一個&作為單目運算符,表示取得某個變量的地址
????????4、定義引用時,一個&表示定義的是左值引用
????????5、定義引用時,兩個&表示定義的是右值引用
8.3 引用的注意事項
1> 引用的使用跟普通變量一樣,直接使用即可
2> 引用在定義時,必須用目標對其進行初始化,否則報錯
3> 引用與引用的目標是同一個內存空間,系統(tǒng)不會為引用單獨分配內存空間
4> 引用和引用的目標一般要求必須是同一數據類型(繼承時除外)
5> 引用一旦指定,后期就不能進行更改目標了
6> 一個目標,可以定義多個引用,多個引用和引用目標都是同一個內存空間
#include <iostream>using namespace std;int main()
{int num = 520; //定義一個普通變量//定義一個引用,目標為num//int &ref_1 ; //定義引用時,必須使用目標為其初始化,否則報錯int &ref_1 = num; //從此,num就有了一個別名 ref_1cout<<"ref_1 = "<<ref_1<<endl; //對數據具有讀功能ref_1 = 1314; //對數據具有寫功能cout<<"ref_1 = "<<ref_1 << " num = "<< num <<endl; //兩個變量名都更改cout<<"&ref_1 = "<<&ref_1 << " &num = "<< &num <<endl; //兩個變量名地址相同cout<<"sizeof(ref_1) = "<<sizeof(ref_1) << " sizeof(num) = "<< sizeof(num) <<endl; //所占內存地址大小相同cout<<"tipe id of ref_1 = "<<typeid (ref_1).name() << " tipe id of num = "<<typeid (num).name()<<endl; //類型相同//double &ref_2 = num; //不同類型的引用不能進行綁定int value = 999;//將ref_1引用到value上ref_1 = value; //使用value的值給ref_1(num)重新賦值cout<<"ref_1 = "<<ref_1 << " num = "<< num << " value = "<<value<<endl;cout<<"&ref_1 = "<<&ref_1 << " &num = "<< &num << " &value = "<<&value<<endl;//一個目標可以定義多個引用int &ref_2 = num;int &ref_3 = ref_1;cout<<"&ref_1 = "<<&ref_1 << " &num = "<< &num << " &ref_2 = "<<&ref_2<<" &ref_3 = "<<&ref_3<<endl;return 0;
}
8.4 引用作為函數的形參(重點)
1> 引用作為函數的形參,就沒有了值傳遞和地址傳遞的概念了,傳遞的就是實參本身
2> 案例
#include <iostream>using namespace std;//定義交換函數1 void swap_1(int num, int key) {int temp = num;num = key;key = temp;cout<<"swap_1::num = "<<num<<" key = "<<key<<endl; //1314 520 }//定義交換函數2 void swap_2(int *ptr, int *qtr) {int *temp = ptr;ptr = qtr;qtr = temp;cout<<"swap_2::*ptr = "<<*ptr<<" *qtr = "<<*qtr<<endl; //1314 520 }//定義交換函數3 void swap_3(int *ptr, int *qtr) {int temp = *ptr;*ptr = *qtr;*qtr = temp;cout<<"swap_3::*ptr = "<<*ptr<<" *qtr = "<<*qtr<<endl; //1314 520 }//定義交換函數4 void swap_4(int &n, int &k) {int temp = n;n = k;k = temp;cout<<"swap_4::num = "<<n<<" key = "<<k<<endl; //520 1314 }/***************************主程序********************/ int main() {int num = 520;int key = 1314;//調用交換函數1swap_1(num, key);cout<<"main::num = "<<num<<" key = "<<key<<endl; //520 1314//調用交換函數2swap_2(&num, &key);cout<<"main::num = "<<num<<" key = "<<key<<endl; //520 1314//調用交換函數3swap_3(&num, &key);cout<<"main::num = "<<num<<" key = "<<key<<endl; //1314 520//調用交換函數4swap_4(num, key);cout<<"main::num = "<<num<<" key = "<<key<<endl; //520 1314return 0; }
8.5 引用作為函數的返回值(重點) ---> 引用函數
1> 引用作為函數的返回值,返回的是一個左值
2> 要求只能是生命周期比較長的變量才能作為返回值結果
3> 生命比較長的成員
????????1、全局變量
????????2、靜態(tài)局部變量
????????3、堆區(qū)空間的內容
????????4、主調函數以地址或者引用的形式傳過來的變量
#include <iostream>using namespace std;//定義一個引用函數
int &fun()
{static int num = 520;cout<<"fun::&num = "<<&num<<endl;return num; //不能返回局部變量的空間 可以返回靜態(tài)局部變量的空間
}//引用函數返回堆區(qū)空間的地址
int &hun()
{return *new int(520); //在堆區(qū)空間申請一個int大小的空間并初始化為520,并將該空間返回
}int main()
{int key = fun(); //此時的key和num是不同的變量cout<<"key = "<<key<<" &key = "<<&key<<endl; //520int &value = fun(); //此時的value和num是同一個變量cout<<"value = "<<value<<" &value = "<<&value<<endl; //520fun() = 1314; //引用函數的返回結果是一個左值cout<<"value = "<<value<<" &value = "<<&value<<endl; //1314int &ref = hun(); //在主程序中獲取被調函數中開辟的堆區(qū)空間cout<<"ref = "<<ref<<endl; //520delete &ref; //釋放堆區(qū)空間return 0;
}
8.6 常引用
1> 對于變量而言,變量的內容既可讀又可寫
2> 但是,有時函數的形參是引用變量時,在函數體內僅僅只是為了讀取數據中的內容,而不
????????是為了更改形參
????????此時,為了保護新參不被修改,我們可以加常屬性 const
3> 引用和目標進行搭配使用
????????1、普通引用 普通變量
????????2、普通引用 常變量
????????3、常引用 普通變量
????????4、常引用 常變量
#include <iostream>using namespace std;int main()
{/**************普通引用 普通變量*******************/int num = 520;int &ref_1 = num;cout<<"num = "<<num<< " ref_1 = "<<ref_1<<endl; //對空間都有讀屬性num = 1314; //普通變量對空間具有寫屬性ref_1 = 999; //普通引用對空間也具有寫屬性/****************普通引用 常變量********************/const int value = 999; //定義一個常變量//int &ref_2 = value; //普通引用不能綁定常變量/***************常引用 普通變量**********************/int temp = 1111; //定義普通變量const int &ref_3 = temp; //常引用的目標為普通變量cout<<"temp = "<<temp<< " ref_3 = "<<ref_3<<endl; //對空間都有讀屬性temp = 222; //普通變量具有寫屬性//ref_3 = 777; //常引用沒有寫屬性/******************常引用 常變量******************/const int key = 444; //定義常變量const int &ref_4 = key; //定義常引用cout<<"key = "<<key<< " ref_4 = "<<ref_4<<endl; //對空間都有讀屬性//key = 666; //沒有寫屬性//ref_4 = 777; //沒有寫屬性return 0;
}
8.7 引用與指針的關系
1> 指針變量也是有8字節(jié)的內存空間,既然有內存空間,就可以給該內存空間起個別名
2> 指針引用的定義格式: 數據類型 * & = 引用目標;
3> 指針引用定義后,使用方式跟原指針一致
#include <iostream>using namespace std;int main()
{int num = 520; //定義普通變量int *ptr = # //定義一個指針變量int * & ptr_ref = ptr; //定義了一個指針的引用,后期 ptr_ref就可以跟ptr一樣被使用cout<<"*ptr = "<<*ptr<<" *ptr_ref = "<<*ptr_ref<<endl; //對數據具有讀功能cout<<"&ptr = "<<&ptr<<" &ptr_ref = "<<&ptr_ref<<endl; //地址一致cout<<"ptr = "<<ptr<<" ptr_ref = "<<ptr_ref<<" &num = "<<&num<<endl; //內容一致int key = 1314;ptr_ref = &key; //ptr = &key; //讓指針變量重新指向新的空間return 0;
}
8.8 引用與數組的關系
1> C語言中,沒有數組的引用,當向一個函數傳遞數組時,本質上使用的是指針接收的參數
2> C++中支持數組引用,表示給數組起個別名
3> 數組引用定義格式:數據類型 (&引用名)[數組長度] = 其他數組名;
#include <iostream>using namespace std;//定義功能函數
void fun(int arr[], int n) //arr接受的是主調函數中數組的起始地址,并不是數組本身
{cout<<"fun::sizeof(arr) = "<<sizeof(arr)<<endl; //?for(int i=1; i<n; i++){for(int j=0; j<n-i; j++){if(arr[j]>arr[j+1]){int temp = arr[j];arr[j] = arr[j+1];arr[j+1] = temp;}}}
}//定義功能函數
void hun(int (&arr)[5]) //該函數中的arr接受的就是主調函數中的數組本身
{cout<<"fun::sizeof(arr) = "<<sizeof(arr)<<endl; //20int n = sizeof(arr)/sizeof (arr[0]); //求數組長度for(int i=1; i<n; i++){for(int j=0; j<n-i; j++){if(arr[j]>arr[j+1]){int temp = arr[j];arr[j] = arr[j+1];arr[j+1] = temp;}}}
}int main()
{int arr[] = {3,8,3,2,4};int len = sizeof(arr)/sizeof (arr[0]);//fun(arr, len);hun(arr);return 0;
}
8.9 右值引用(了解)
1> 右值引用的引入目的:為了解決左值引用的瑕疵問題
2> 左值引用只能引用左值目標,右值引用只能引用右值目標
3> 右值引用定義格式:數據類型 &&引用名 = 右值目標;
#include <iostream>using namespace std;//定義求最值函數
int my_max(int &m, int &n)
{return m>n?m:n;
}int my_max(int &&m, int &&n)
{return m>n?m:n;
}int main()
{int num = 5;int key = 3;int res = my_max(num,key);my_max(100,200); //不能傳遞,原因是形參是左值引用,左值引用只能引用左值int &&ref_1 = 520; //右值引用可以引用常量、臨時值、將亡值//int &&ref_2 = num; //右值引用只能引用右值,不能引用左值int &&ref_3 = move(num); //使用move函數,將左值移動成右值return 0;
}
8.10 引用與指針的區(qū)別(重點)
1> 可以有指針數組,但是沒有引用數組
2> 引用必須初始化,但是指針可以不用初始化
3> 指針擁有獨立的內存空間,而引用沒有,引用與其目標共用同一塊內存
4> 指針可以改變指向,但是引用一旦指定目標,后期不能更改
5> 引用不能為空,可以有空指針
6> 指針進行算術運算時,是對地址空間進行偏移,而引用進行算術運算時,就是目標進行的算術運算
7> 指針有二級指針,但是沒有二級引用
8> 指針的目標必須是左值的地址,而引用可以有左值引用也可以有右值引用
9> 有萬能指針,但是沒有萬能引用
九、C++對C語言的函數的擴充
9.1 函數重載(overload)
1> 引入目的:程序員在定義函數時,有時僅僅是因為函數的參數不同,導致同一功能的函數
????????需要定義多個。例如,求兩個整數的和、兩個小數的和、兩個字符串的和等等,函數內
????????部實現(xiàn)邏輯都一樣,僅僅只是因為函數參數類型不同,導致需要定義多個函數
2> C++中引入函數重載的概念,表示能夠在同一個作用域下定義多個同名的函數
3> 要求:
????????1、作用域相同
????????2、函數名相同
????????3、形參列表必須不同(參數個數、參數類型)
????????4、跟返回值沒有關系
4> 當調用函數時,系統(tǒng)會根據實參的類型和個數,自動匹配相關函數進行調用
#include <iostream>using namespace std;int sum(int m, int n) //定義求兩個整數的和{return m+n;}//求兩個小數的和double sum(double m, double n){return m+n;}float sum(float m, float n){return m+n;}//求兩個字符串的和string sum(string m, string n){return m+n;}int main()
{cout << sum(3,5) << endl; //8cout << sum(3.3,5.5) << endl; //8.8cout << sum("3","5") << endl; //35return 0;
}
練習:定義兩個整數求最大值、兩個小數求最大值、兩個字符串求最大值函數,并完成相關的測試
9.2 默認參數
1> 引入背景:
????????程序員在定義函數時,有某個參數或者某幾個參數,可以由主調函數傳遞,也可以不需
????????要主調函數傳遞時,此時就可以定義默認參數,對于設置了默認參數的形參變量,如果
????????主調函數傳遞該數據,那么就使用主調函數中傳遞的數據,如果主調函數不傳遞數據,
????????那么就使用默認提供的參數
2> 設置格式:返回值類型 函數名 (參數1, 參數2=初始值, 參數3=初始值)、
3> 函數形參的默認參數的設置要求:靠右原則,只有某個參數的右側的所有的形參都設置了
????????初始值,當前這個形參才能設置
????????因為函數的實參向形參傳遞的方向是靠左原則
4> 當包含默認參數的函數和函數重載同時出現(xiàn)時,應避免重復性定義
5> 當分文件定義時,默認參數的設置需要寫在聲明部分,定義部分就不需要寫了
#include <iostream>using namespace std;int sum(int = 100, int = 100, int = 100); //函數聲明//這個函數與上個函數重載,定義的使用沒有問題
/*
int sum(int num, int key)
{return num+key;
}*/int main()
{cout << sum(1,2,3) << endl; //6cout << sum(1,2) << endl; //103 //調用時出問題cout << sum(1) << endl; //201cout << sum() << endl; //300return 0;
}//定義三個數求和函數
int sum(int m, int n, int k)
{return m+n+k;
}
9.3 內聯(lián)函數
1> C++支持內聯(lián)函數:設置了內聯(lián)函數的函數,會建議編譯器在編譯時將函數體展開
????????由于是在編譯階段,在被調函數處展開,那么在運行時,就無需再為該函數分配內存空
????????間了? ? ? 能夠大大提高運行效率
2> 定義格式:在定義函數前加關鍵字 inline
3> 要求:
????????1、函數體較小,否則會造成主程序膨脹
????????2、調用比較頻繁
????????3、遞歸函數不允許設置成內聯(lián)函數
????????4> 有時,即使設置了內聯(lián)函數,也不一定會在編譯時展開
#include <iostream>using namespace std;//該函數就是內聯(lián)函數
inline int sum(int = 100, int = 100, int = 100); //函數聲明//這個函數與上個函數重載,定義的使用沒有問題
/*
int sum(int num, int key)
{return num+key;
}*/int main()
{cout << sum(1,2,3) << endl; //6cout << sum(1,2) << endl; //103 //調用時出問題cout << sum(1) << endl; //201cout << sum() << endl; //300return 0;
}//定義三個數求和函數
inline int sum(int m, int n, int k)
{return m+n+k;
}
9.4 啞元
1> 引入背景:程序員在定義函數時,有時某個形參或者某幾個形參,在函數體內沒有實質性的作用,但是,也還需要一個參數進行占位,此時就可以定義該參數為啞元。
2> 定義格式:定義形參時,只給類型,不給形參名,函數體內也不用
3> 使用場景:
1、程序優(yōu)化:原本程序調用某個函數需要5個參數,但是,發(fā)布運行一段時間后,由于技術的革新,導致該函數只需要3個參數就能正常運行,但是,由于該函數在整個程序很多地方都已經被調用了,如果直接改變該函數的參數個數,那么需要將每個調用函數處都進行修改,非常不方便,此時,就可以使用啞元,僅僅只是占位作用
2、在進行自增或自減運算符重載時,使用啞元用于區(qū)分是前置還是后置(后期講)
#include <iostream>using namespace std;//此時第二個和第三個參數就是啞元,唯一的作用就是占位作用
int sum(int m, int , int , int g)
{return m+g;
}int main()
{cout << sum(2,3,4,5) << endl;cout << sum(2,3,4,5) << endl;cout << sum(2,3,4,5) << endl;cout << sum(2,3,4,5) << endl;cout << sum(2,3,4,5) << endl;cout << sum(2,3,4,5) << endl;cout << sum(2,3,4,5) << endl;cout << sum(2,3,4,5) << endl;cout << sum(2,3,4,5) << endl;cout << sum(2,3,4,5) << endl;return 0;
}
?思維導圖