C++ Primer Plus 筆記第七章
復習函數基本知識:
要使用C++函數,要完成工作:
1. 提供函數基本知識;
2. 提供函數原型;
3. 調用函數
庫函數是已經定義和編譯好的函數,同時可以使用標準庫頭文件提供其原型
eg:標準頭文件 cstring 中包含了 strlen() 和其它一些字符串相關的函數原型
函數原型和函數調用:
函數原型描述了函數到編譯器的接口,將函數返回值的類型及參數類型和數量告訴編譯器;
double cube ( double x ); or double cube ( double );
原型的功能:
編譯器正確處理函數返回值;
編譯器檢查使用的參數數目是否正確;
編譯器檢查使用參數類型是否正確,如果不正確,轉換為正確的類型(可能的話)
函數和數組:
int sum_arr ( int * arr,int n ) // 數組的元素為 int 型,因此 正確的函數頭傳遞參數為 int * arr
int sum_arr ( int arr[ ],int n ) // 用 int arr[ ] 替換 int * arr ,含義相同。同時 int arr[ ] 還提醒用戶 arr 不僅指向 int 而且是指向數組的第一個 int
在大多數情況下,C++和C語言一樣,將數組名視為指針,存在兩種例外:
1. 數組聲明,使用數組名來標記存儲位置;
2. 對數組名使用 sizeof 將得到整個數組的長度
函數傳遞數組時,將數組的位置(地址)、包含的元素種類(類型)以及元素的數目(n變量)提交給函數;
函數傳遞常規變量時,函數將使用變量的拷貝,但使用數組時,函數使用原來的數組;
可以在被傳遞函數中,數組參數使用 const 限定符,保證原始數組數據不被修改(只讀傳入)
int show_array ( const double arr[ ],int limit ); // 函數原型
鍵盤輸入數組並顯示輸入數組例程:
1 #include<iostream> 2using namespace std; 3 int fill_array(double ar[], int limit); 4 void show_array(const double ar[],int n); // const 可以防止修改數組內容 5 6 int main() 7 { 8 const int arsize = 5; 9 double ar[arsize]; 10 int n; 11 n = fill_array(ar, arsize); // 數組調用,將數組的首地址作為實參 12 //cout << n<<endl; 13 show_array(ar, n); 14 } 15 16 // 輸入數組 17 int fill_array(double ar[], int limit) 18 { 19 double res; 20 int i; 21 for ( i = 0; i < limit; i++) 22 { 23 cout << "Enter value #" << i + 1 << ": "; 24 while (!(cin >> res)) // 判斷是否正確讀入數字,如果沒有,進入循環 25 { 26 cin.clear(); // 重置輸入,如果省略,程序將拒絕繼續讀取輸入 27 while (cin.get() != ‘\n‘) // 清除輸入流中所有的錯誤輸入到‘\n‘ 28 continue; 29 cout << "wrong! please enter number: \n" 30 << "Enter value #" << i + 1 << ":"; 31 } 32 if (res < 0) // 負值輸入將提前結束數組的輸入 33 break; 34 ar[i] = res; 35 } 36 return i; 37 } 38 // 顯示數組 39 void show_array(const double ar[], int n) 40 { 41 for (int i = 0; i < n; i++) 42 cout << ar[i] << endl; 43 }
自下而上的程序設計:
通過數據類型和設計適當的函數來處理數據,然後將這些函數組合成一個程序;
適合於OOP——它首先強調的數據表示和操縱
使用數組區間的函數:
對於處理數組的C++函數,必須將處理數組中的種類、數組的起始位置和數組中元素數量交給函數:
1. 將數組的起始處的指針作為一個參數,將數組的長度作為第二個參數;
2. 指定元素區間,通過傳遞兩個指針完成,一個標識數組開頭,一個標識數組尾部
eg:
sum = sum_arr ( arr, arr+3 ); // 函數調用,實參傳入地址區間
int sum ( const int* begin,const int* end );// 函數頭,形參為兩個指向數組類型的指針(int*)
指針和 const:
可以使用兩種不同的方式將 const 關鍵字用於指針:
1. 讓指針指向一個常量對象,防止使用該指針修改所指向的值;
2. 將指針本身聲明為常量
聲明一個指向常量的指針:
int age = 39;
const int * pt = &age;
聲明中 pt 指向一個 const int 因此不能使用 pt 修改這個值—— *pt 為常量不能修改
還可以將 const 變量的地址賦給指向const的指針,不能將 const 的地址賦給常規的指針
記住:如果數據類型不是指針,可以將 const 數據或非 const 數據的地址指向 const 指針
不能將 const 數據賦給非 const 指針
int * const finger = &sloth:
指針 finger 本身被聲明為 const,使得 finger 只能指向 sloth,但允許使用 finger 來修改 sloth 的值
函數和二維數組:
數組作為參數的函數,數組名被視為地址,相應的形參應為一個指針;
int data[3][4] = { {1,2,3,4}, {9,8,7,6}, {2,4,6,8} }; // 聲明
int tatal = sum(data, 3); // 調用
兩種函數原型(形參的形式):
int sum (int (*ar2) [4],int size ); // 聲明一個由4個指向 int 的指針組成的數組,括號不能省
int sum (int ar2[ ][4],int size); // 可讀性更強
在函數定義中使用二維數組,最簡單的辦法是將 ar2 看作一個二維數組的名稱
ar2 實際上是一個指針,必須對 ar2 執行兩次解除引用才能得到數據:
最簡單的方式:ar2[r][c];
ar2[r][c] = *(*(ar2 + r) + c); // same thing
函數和C-風格字符串:
將字符串作為參數時意味著傳遞的是地址;
表示字符串的方式有3種:
1. char 數組
2. 用引括號起的字符串常量
3. 被設置為字符串的地址的 char 指針
將字符串作為參數來傳遞,實際傳遞的是字符串的第一個字符的地址,形參聲明應為 char*:
int c_in_str (const char * str,char ch); // 使用指針表示法
int c_in_str (const char str[],char ch); // 也可以使用數組表示法
處理字符串中字符的標準方式:
1 while (*str) // until *stt == ‘\0‘ 2 { 3 statement; 4 str++ // 將指針增加一個字節 5 }
返回C-風格字符串的函數:
函數無法返回一個字符串,但是可以返回字符串的地址;
char * buildstr ( char c,int n )
函數和結構:
為結構編寫函數比為數組編寫函數簡單,結構變量相比於數組更接近於單值變量;
函數可以使用原始結構的拷貝,也可返回結構(因為結構可以互相賦值);
結構名只是結構的名稱,要獲得結構的地址,必須使用地址操作符 &;
處理結構的函數例程:
1 #include<iostream> 2 #include<cmath> 3 using namespace std; 4 5 struct polar { 6 double distance; 7 double angle; 8 }; 9 struct rect { 10 double x; 11 double y; 12 }; 13 polar rect_to_polar(rect xypose); 14 void show_polar(polar rrpose); 15 int main() 16 { 17 polar rppose; 18 rect xypose; 19 cout << "Enter pose x and y or type ‘q‘ to quiz: "; 20 while (cin>>xypose.x>>xypose.y) 21 { 22 23 rppose = rect_to_polar(xypose); 24 show_polar(rppose); 25 cout << ("Continue Enter x and y or type ‘q‘ to quiz: "); 26 27 } 28 return 0; 29 } 30 31 polar rect_to_polar(rect xypose) 32 { 33 polar rppose; 34 rppose.distance = sqrt(xypose.x*xypose.x + xypose.y*xypose.y); 35 rppose.angle = atan2(xypose.y, xypose.x); 36 return rppose; 37 } 38 39 void show_polar(polar rppose) 40 { 41 const double rad_to_deg = 57.29577951; 42 43 cout << "distance = " << rppose.distance << endl; 44 cout << "angle = " << rppose.angle*rad_to_deg << " degrees\n"; 45 46 }
傳遞結構地址例程:
與傳遞結構本身不同之處:
1. 調用函數時,將結構地址(&pplace)而不是結構本身(pplace);
2. 將形參聲明為指向 polar 的指針,即 polar* 類型;
3. 形參指針而不是結構,因此應使用間接成員操作符(->)而不是(.);
4. 傳遞地址而不是復制,函數可能不在需要返回值,使用 void
1 #include<iostream> 2 #include<cmath> 3 using namespace std; 4 5 struct polar { 6 double distance; 7 double angle; 8 }; 9 struct rect { 10 double x; 11 double y; 12 }; 13 void rect_to_polar(const rect * xypose, polar* rppose); 14 void show_polar(const polar * rppose); 15 int main() 16 { 17 polar rppose; 18 rect xypose; 19 cout << "Enter pose x and y or type ‘q‘ to quiz: "; 20 while (cin >> xypose.x >> xypose.y) 21 { 22 23 rect_to_polar( &xypose, &rppose ); 24 show_polar( &rppose ); 25 cout << ("Continue Enter x and y or type ‘q‘ to quiz: "); 26 27 } 28 return 0; 29 } 30 31 void rect_to_polar(const rect* xypose, polar * rppose) 32 { 33 rppose->distance = sqrt(xypose->x*xypose->x + xypose->y*xypose->y); 34 rppose->angle = atan2(xypose->y, xypose->x); 35 } 36 37 void show_polar(const polar * rppose) 38 { 39 const double rad_to_deg = 57.29577951; 40 41 cout << "distance = " << rppose->distance << endl; 42 cout << "angle = " << rppose->angle*rad_to_deg << " degrees\n"; 43 44 }
函數和 string 對象:
C++如果需要多個字符串,可以聲明一個 string 對象數組,而不是二維 char 數組;
以下例程聲明了一個 string 對象數組,並將該數組傳遞給一個函數以顯示其內容:
1 #include<iostream> 2 #include<string> 3 using namespace std; 4 const int SIZE = 5; 5 void display(const string list[], int n); 6 7 int main() 8 { 9 string list[SIZE]; // 聲明 string 數組,每一個元素為一個 string 對象 10 cout << "Enter your " << SIZE << " favotite astronomical sights: \n"; 11 for (int i = 0; i < SIZE; i++) 12 { 13 cout << i + 1 << ": "; 14 getline(cin, list[i]); // 讀取一個字符串 15 } 16 17 cout << "Your list: \n"; 18 display(list, SIZE); 19 20 return 0; 21 } 22 23 void display(const string list[], int n) 24 { 25 for (int i = 0; i < n; i++) 26 cout << list[i] << endl; 27 }
該例程中,除了函數 getline() 外,程序像對待內置類型(int)一樣對待 string 對象
遞歸:
函數調用自己成為遞歸;
每個遞歸都創建自己的一套變量;
函數指針:
可以編寫將另一個函數的地址作為參數的函數;
這種方法與調用函數相比,允許在不同的時間傳遞不同的函數地址;
函數指針基礎知識:
獲取函數地址;
聲明函數的指針;
使用函數指針來調用函數
1. 獲取函數地址
只要使用函數名即可,如果 think() 是一個函數,則 think 就是該函數的地址
要區分函數的地址和函數的返回值:
process ( think ); // 參數為函數地址,使得函數 process 能夠在函數內部調用 think() 函數
thought ( think() ); // 參數為函數的返回值,先調用 think() 函數,其返回值傳給 thought
2. 聲明函數指針
聲明函數指針時,必須指定指針指向的函數類型:
double pam ( int ); // 原型
double ( *pf ) ( int ); // 指針類型聲明,將 pam 替換為了 (*pf),(*pf)是函數,pf 就是函數指針
pf = pam; // 將相應的函數地址賦給指針
提示: 要聲明指向特定類型的函數指針,可以先編寫這種函數的原型,然後用(*pf)替換函數名
註意: 函數地址賦給函數指針時,特征標和返回返回類型必須相同
3. 使用指針來調用函數
void estimate ( int lines,double (*pf) (int) ); // 函數原型
estimate ( 50, pam); // 讓 estime() 使用 pam() 函數
(*pf) 扮演的角色與函數名相同,使用(*pf)時,只需要將它看成函數名
double pam ( int );
double (*pf) (int);
pf = pam; // 函數指針指向函數 pam()
double x = pam (4); // 使用函數名調用函數pam()
double y = (*pf) (5); // 使用函數指針調用函數pam()
double y = pf(5); // C++允許像使用函數名那樣使用 pf
復習題:
函數 judge() 的返回值為 int,他將這樣一個函數地址作為參數:將 const char 指針作為參數,返回一個 int 值,編寫函數原型:
int judge ( int (*pf) ( const char * ) )
C++ Primer Plus 筆記第七章