一個c++程序可以包含很多個函數(shù),源代碼可能會很長,可以將這些函數(shù)分散保存在不同的源文件中,以多文件結(jié)構(gòu)的形式來組織和管理源代碼。
程序員可以在c++源程序中插入一些特殊指令,其作用是告訴編譯器該如何編譯本程序,正式編譯源程序之前,編譯器將預先處理這些特殊指令,因此它們被稱為編譯預處理指令。
本章將介紹c++語言中幾種特殊形式的函數(shù),以及由c++編譯系統(tǒng)為程序員提供的一些常用函數(shù),這些函數(shù)被統(tǒng)稱為系統(tǒng)函數(shù)。
程序設(shè)計可能會面臨比較復雜的數(shù)據(jù),這時程序員需要基于基本數(shù)據(jù)類型來自己定義新的數(shù)據(jù)類型,這就是自定義數(shù)據(jù)類型。數(shù)組就是一種自定義數(shù)據(jù)類型,本章講講解幾種常用的自定義數(shù)據(jù)類型
一、c++源文件的多文件結(jié)構(gòu)
一個c++程序可以包含很多個函數(shù),源代碼可能會很長,可以將這些函數(shù)分散保存在不同的源文件中,以多文件結(jié)構(gòu)的形式來組織和管理源代碼。
結(jié)構(gòu)化程序設(shè)計,將一個復雜任務分解成多個模塊,編碼時,講每個模塊定義成一個函數(shù),基于團隊協(xié)作開發(fā)程序時,可以將不同模塊的程序設(shè)計和編碼任務交由不同的程序員去完成,程序員各自獨立編程,將所編寫的源程序代碼保存在自己的源文件中,這樣可以互不干擾,基于團隊協(xié)模式開發(fā)的c++程序會自然形成多文件結(jié)構(gòu)
工程:一個程序開發(fā)項目又稱為工程,一個工程通常包含多個源程序文件,c++語言的源程序文件名為.cpp。一個源程序文件可以包括多個函數(shù),一個函數(shù)只能集中放在一個源程序文件中,不能將函數(shù)定義分開存放在不同文件中,一個程序工程中可以包括很多個函數(shù),但是只有一個主函數(shù),函數(shù)名必須為main()
編譯器:使用編譯器將c++語言翻譯成機器語言,編譯時,同一源程序文件中的所有函數(shù),被統(tǒng)一編譯,因此一個源程序文件被稱為一個編譯單元,每個源程序編譯后都生成一個目標程序,目標程序的擴展名通常為.obj,目標程序是機器語言的程序,機器語言與cpu相關(guān),相同的c++源程序,可以被不同編譯器編譯,生成不同機器語言的目標程序,從而運行于不同類型的cpu之上,
連接器:每個源程序文件編譯后都生成一個目標程序文件,使用連接器,將多個目標程序連接在一起,最終生成一個可執(zhí)行程序文件。在windows操作系統(tǒng)上,可執(zhí)行操作文件的擴展名通常為.exe,可執(zhí)行程序是最終的程序,可以被計算機硬件執(zhí)行。軟件產(chǎn)品銷售的是可執(zhí)行程序,而源程序則是軟件廠家的機密??蓤?zhí)行程序文件可以復制、分發(fā)安裝并執(zhí)行,但很難被閱讀修改。
多文件結(jié)構(gòu):在多文件程序中,一個文件定義的函數(shù),可以被其他文件函數(shù)調(diào)用??梢员黄渌募{(diào)用的函數(shù)稱為外部函數(shù),一個文件中定義的全局變量,也可以被其他文件中的函數(shù)訪問,可以被其他文件訪問的全局變量,被稱為外部全局變量。換句話說,一個文件中的函數(shù),可以調(diào)用其他文件中的外部函數(shù), 也可以訪問其他文件中的外部全局變量,調(diào)用外部函數(shù)時,需要先申明再調(diào)用,訪問外部全局變量時,也需要現(xiàn)聲明再訪問,聲明的作用,是將外部函數(shù)或外部全局變量的作用域,延申到本程序文件中來。下面以養(yǎng)魚池造價測試程序進行拆分說明,多文件結(jié)構(gòu)的具體語法和規(guī)則:
源程序文件1.cpp:內(nèi)容包括全局變量和主函數(shù)的定義,假設(shè)由程序員甲所編寫。
#include <iostream>
using namespace std;
void RectCost();
double CircleCost(double r);
double length,width; //? 全局變量:分別保存長方形養(yǎng)魚池的長寬
double r1,r2; //? 全局變量:分別保存圓形清水池和污水池的半徑
double totalCost = 0; //? 全局變量:用于保存最終結(jié)果,即總造價
int main()
{
/*下來語句將使用鍵盤輸入原始數(shù)據(jù),保存到對應的原始變量中*/
cout <<"請輸入長方形的長寬:";
cin>>length>>width;
cout<<"請輸入清水池和污水池的半徑:";
cin>>r1>>r2;
RectCost();//調(diào)用函數(shù)RectCost計算長方形言語此造價
totalCost += CircleCost(r1);//調(diào)用CircleCost計算清水池造價
totalCost += CircleCost(r2);//再次調(diào)用CircleCost
cout<<"工程總造價為:"<<totalCost<<endl;
return 0;
}
源程序2.cpp:包括兩個用于計算的子函數(shù),由程序員乙編寫
extern double length,width;//聲明外部全局變量:分別保存長方形養(yǎng)魚池的長寬
extern double totalCost;//聲明外部全局變量:用于保存最終計算結(jié)果,即總造價
void RectCost()
{
double cost;
cost = length*width*10; //直接讀取全局變量length和width中的數(shù)據(jù)
totalCost+=cost;//將計算結(jié)果直接累加到全局變量totalCost中。
}
double CircleCost(double r)
{
double cost;
cost = 3.14*r*r*10;
return cost;
}
1.1 聲明外部函數(shù)原型和外部全局變量的語法細則
-
聲明外部函數(shù)原型
聲明外部函數(shù)原型,使用extern關(guān)鍵字,聲明 外部函數(shù)時,extern關(guān)鍵字也可以省略。
extern void RectCost();//聲明RectCost函數(shù) void RectCost(); //extern關(guān)鍵字可以省略。使用extern關(guān)鍵字的作用是更明確的指出,所聲明的函數(shù)是一個外部函數(shù),
-
聲明外部全局變量
聲明外部全局變量時,必須加上extern關(guān)鍵字,不能初始化。否則就變成了另外一條全局變量定義語句。連接時,會出現(xiàn)和其他文件定義的全局變量重名的錯誤。
extern double totalCost; //聲明外部全局變量totalCost double totalCost; //語法錯誤,不能省略extern關(guān)鍵字 extern double totalCost = 0; //語法錯誤,外部全局變量不能初始化 外部函數(shù)可以只聲明不調(diào)用,外部全局變量可以只聲明不訪問,也就是說,只聲明不使用不會引起語法錯誤。
1.2 多文件結(jié)構(gòu)
- 源程序文件所定義的函數(shù)默認都是外部函數(shù),可以被其他文件中的函數(shù)調(diào)用;所定義的全局變量默認都是外部全局變量,也可以被其他文件中的函數(shù)訪問。
- 外部函數(shù)和外部全局變量被多文件結(jié)構(gòu)中的所有文件共享,其他文件只要經(jīng)過聲明都可以使用這些函數(shù)和全局變量
- 多文件結(jié)構(gòu)中,所有的外部函數(shù)不能重名,所有的外部全局變量不能重名,所有的外部函數(shù)和外部全局變量之間也不能重名
某些情況下,一個文件中定義的某些函數(shù)或全局變量只供文件內(nèi)部的其他函數(shù)使用,c++語言可以將這些函數(shù)或全局變量定義成靜態(tài)的,即靜態(tài)函數(shù)或靜態(tài)全局變量,另外c++語言還可以定義靜態(tài)局部變量。
靜態(tài)函數(shù)與靜態(tài)變量:
-
靜態(tài)函數(shù)
定義函數(shù)時,再函數(shù)頭前加static關(guān)鍵字,該函數(shù)就被定義為靜態(tài)函數(shù)。靜態(tài)函數(shù)只能被本文件內(nèi)部的其他函數(shù)調(diào)用,其他文件不能調(diào)用,即使經(jīng)過聲明也不行。
static void RectRoct() { ... ... }static關(guān)鍵字,將所定義的函數(shù)作用域,限制在本文件范圍內(nèi),禁止延申到其他文件,合理定義靜態(tài)函數(shù),可以防止其他文件對該函數(shù)的誤調(diào)用。不同文件中的靜態(tài)函數(shù)可以重名
-
靜態(tài)全局變量
全局變量具有文件作用域,靜態(tài)和非靜態(tài)指的是他們能否被其他文件中的函數(shù)訪問。
定義全局變量時,在語句前加static關(guān)鍵字,該變量就被定義為靜態(tài)全局變量,靜態(tài)全局變量只能被本文將內(nèi)的函數(shù)訪問,其他文件不能訪問,即使經(jīng)過聲明也不行。
static double r1,r2;static關(guān)鍵字,將所定義的全局變量作用域,限制在本文件范圍內(nèi),禁止延申到其他文件,合理定義靜態(tài)全局變量,可以防止其他文件對該全局變量的誤操作。不同文件中的靜態(tài)全局變量可以重名
-
靜態(tài)局部變量
局部變量只具有塊作用域,只能在本函數(shù)內(nèi)部使用、訪問,任何時候都不能被其他函數(shù)訪問,更別說其他文件中的函數(shù),c++語言中,局部變量有靜態(tài)和非靜態(tài)之分,但含義與全局變量相比較,是不一樣的。
定義方法:在函數(shù)體或復合語句中定義的變量就是局部變量,在函數(shù)體或復合語句中定義局部變量時,在語句前加“static”關(guān)鍵字,該變量就被定義為靜態(tài)局部變量。
作用域:在源程序中具有塊作用域(與普通局部變量相同)
內(nèi)存分配:普通的局部變量在執(zhí)行時,是自動分配內(nèi)存的,而靜態(tài)局部變量是靜態(tài)分配內(nèi)存,就是程序執(zhí)行時立即分配內(nèi)存,一直到程序執(zhí)行結(jié)束才釋放;存放在靜態(tài)儲存區(qū)(與全局變量相同)。
綜上所述,靜態(tài)局部變量其作用域與局部變量相同,程序執(zhí)行時,其內(nèi)存生存期和存放位置與全局變量一樣。也就是說,靜態(tài)局部變量是一種居于全局變量和局部變量之間的一種折中變量。
演示程序:對比靜態(tài)和非靜態(tài)局部變量
#include <iostream> using namespace std; void func() { int x=0; //定義普通局部變量(非靜態(tài))x,初始化為0 static int y=0;//定義靜態(tài)局部變量y,初始化為0 x++; y++; //對x,y同樣做加1操作。 cout<<x<<"and"<<y<<endl; } int main() { /* x在動態(tài)儲存區(qū),計算機執(zhí)行到其定義語句時,才分配內(nèi)存,函數(shù)調(diào)用結(jié)束,其內(nèi)存即被釋放,第一次運行func結(jié)束時,x的值就被丟失了。再次調(diào)用時,從新賦值。 y儲存在靜態(tài)內(nèi)存區(qū),程序加載時即分配內(nèi)存,并初始化為0,其內(nèi)存釋放要等到源程序結(jié)束。因此第一次調(diào)用結(jié)束釋放func的棧幀,不會影響到y(tǒng)的值。 */ func(); //調(diào)用函數(shù)func,函數(shù)中顯示x=1,y=1, func(); //再次調(diào)用func,函數(shù)中顯示x=1,y=2, return 0; }c++語言中的static關(guān)鍵字,是一個多義詞,使用這個關(guān)鍵字定義局部變量和全局變量時,其含義是不一樣的,程序員應該根據(jù)上下文來推斷它的含義。
1.3 頭文件
團隊協(xié)作開發(fā)時,加上某個程序員編寫了一個c++程序文件(假設(shè)為1.cpp),其中定義了一組函數(shù),也定義了若干全局變量。其他程序員需要訪問1.cpp中的函數(shù)或全局變量,就需要對這些函數(shù)或全局變量進行聲明,訪問多少個外部函數(shù)或全局變量,就需要寫多少條聲明語句。對于項目組的所有程序員,只要訪問1.cpp中的函數(shù)或全局變量,就都需要在自己的程序文件中編寫聲明語句。編寫這些聲明語句時重復而枯燥的工作,為此c++語言引入了頭文件(header)的概念。
程序員甲在編寫好1.cpp源程序文件后,另外再編寫一個頭文件,其中包含1.cpp所有外部函數(shù)和全局變量的聲明語句。習慣上將這個頭文件命名為1.h(或1.hpp),即與源文件同名,擴展名為.h或.hpp,項目組的其他程序員需要訪問1.cpp中的函數(shù)或全局變量,只要再自己的程序文件增加語句:#include "1.h",該語句的作用就是將頭文件1.h中的所有聲明語句自動插入到該語句位置,這就消除了以往一條一條手工編寫聲明語句的繁瑣。
#include <iostream>
using namespace std;
#include "2.h" //用于替代下方的函數(shù)聲明語句。
// void RectCost();
// double CircleCost(double r);
double length,width; //? 全局變量:分別保存長方形養(yǎng)魚池的長寬
double r1,r2; //? 全局變量:分別保存圓形清水池和污水池的半徑
double totalCost = 0; //? 全局變量:用于保存最終結(jié)果,即總造價
int main()
{
/*下來語句將使用鍵盤輸入原始數(shù)據(jù),保存到對應的原始變量中*/
cout <<"請輸入長方形的長寬:";
cin>>length>>width;
cout<<"請輸入清水池和污水池的半徑:";
cin>>r1>>r2;
RectCost();//調(diào)用函數(shù)RectCost計算長方形言語此造價
totalCost += CircleCost(r1);//調(diào)用CircleCost計算清水池造價
totalCost += CircleCost(r2);//再次調(diào)用CircleCost
cout<<"工程總造價為:"<<totalCost<<endl;
return 0;
}
/* "2.h"頭文件聲明 */
void RectCost();
double CircleCost(double r);
頭文件的內(nèi)容主要包含,外部函數(shù)的申明,外部全局變量的申明,還包含一些共用的符號常量定義等等。插入頭所使用的include指令是一種特殊指令,被稱為編譯預處理指令。
二、編譯預處理指令
程序員可以再c++源程序中插入一些特殊指令,其作用時告訴編譯器如何編譯本程序。正式編譯源程序之前,編譯器預先處理這些特殊指令,他們被稱為編譯預處理指令,例如插入文件頭所使用的"#include"指令
編譯預處理指令,不屬于c++語言的主體,是其附屬組成部分,其作用時方便程序員使用c++語言編程,常用的編譯預處理指令有:
- 文件包含指令
- 宏定義指令
- 條件編譯指令
在c++源程序中,編譯預處理指令可以寫在代碼的任意位置,每條指令單獨寫一行,必須以井號"#"開頭,不加分號";"結(jié)束符。
2.1 文件包含指令:#include
編寫c++源程序時,程序員可以使用文件包含指令(#include)將某個指定文件的內(nèi)容插入到程序代碼的任意位置,通常是用于將某個頭文件(.h)插入到源程序文件(.cpp)中。
編譯一個c++源程序時,c++編譯器會首先進行預處理,預處理時,編譯器將指定文件的內(nèi)容插入該代碼位置,詳細語法如下:
//編譯預處理指令語法:文件報告指令
#include <文件名> //缺省路徑時,編譯器將到標準目錄下搜索指定的文件。
#include "文件名" //缺省路徑時,編譯器將首先在當前目錄下搜索,如果找不到指定文件,編譯器將到標準目錄下搜索指定的文件。
- 文件名:通常不寫完整的全路徑文件名,而使用缺省值路徑,只寫頭文件名,通常頭文件被集中存放在2個目錄下,一是c++編譯器安裝目錄下的"include"子目錄,該目錄稱為標準目錄,二是源程序文件所在的目錄,稱為當前目錄
- #include <文件名>:缺省路徑時,編譯器將到標準目錄下搜索指定的文件。
- #include "文件名":缺省路徑時,編譯器將首先在當前目錄下搜索,如果找不到指定文件,編譯器將到標準目錄下搜索指定的文件。
- 文件必須以井號"#"開頭,不加分號";"結(jié)束符。
2.2 宏定義指令:#define
用一個標識符來表示一段代碼文本,這就稱為一個宏(macro),其中的標識符稱為宏名,所表示的代碼文本稱為宏文本,宏需要先定義再使用,習慣上,宏名用大寫字母來命名。程序員編寫指令時,使用宏定義指令#define來定義一個新的宏,這樣凡是再后續(xù)代碼中,需要用到宏代碼的地方都可以用宏名來代替。宏可以使代碼更加簡潔,易于閱讀。
宏定義指令是一個編譯預處理指令,預處理時,編譯器將源程序中所有的宏名自動替換回原來的宏文本,這稱為宏替換或宏展開。c++語言有3種形式的宏:
- 無參宏
- 有參宏
- 空宏
已經(jīng)定義的宏,可以用宏刪除指令(#undef)刪除。
實例代碼:定義無參宏
#define 宏名 宏文本
#define PI 3.14 //將常量3.14定義為符號常量PI
- 宏名需復合標識符的命名規(guī)則
- 宏文本指定宏名所表示的字符串,可以由任意字符組成,不需要加引號。
無參宏主要用于定義符號常量。
#include <iostream>
using namespace std;
#define PI 3.14 //將常量3.14定義為符號常量PI
int main()
{
double r;
cout<<"請輸入圓的半徑:";
cin>>r;
cout<<"圓的面積是:"<<PI*r*r<<endl; //用符號常量PI代替3.14
cout<<"圓的周長是:"<<PI*2*r<<endl; //用符號常量PI代替3.14
rerurn 0;
}
實例代碼:有參宏
#define 宏名(參數(shù)列表) 宏文本
#define AREA(x) 3.14159*x*x
- 宏名:復合標識符的命名規(guī)則
- 參數(shù)列表:指定若干可被替換的參數(shù),參數(shù)間用逗號","隔開,
- 宏文本:指定宏名所表示的字符串,可以由任意字符組成,不需要加引號,字符串通常是包含參數(shù)的表達式
利用有參宏,可以實現(xiàn)簡單的函數(shù)功能。通過實例代碼,可以寫出一個計算圓面積程序。
#include <iostream>
using namespace std;
#define AREA(x) 3.14159*x*x
int main()
{
int r=5;
cout<<"圓的面積為:"<<3.14159*r*r<<endl;
cout<<"圓的面積為:"<<AREA(r)<<endl; //使用宏進行簡寫。
return 0;
}
使用有參宏的實參可以是表達式,例如:
AREA(3+4) //執(zhí)行預處理時,會展開為3.14159*3+4*3+4,這顯然時不對的, 這是因為定義有參宏時的沒有考慮到運算符計算的優(yōu)先級問題。
/*從新定義AREA有參宏*/
#define AREA(x) 3.14159*(x)*(x)
AREA(3+4) //執(zhí)行預處理時展開為3.14159*(3+4)*(3+4)
實例代碼:空宏
#define 宏名
- 宏名需要符合標識符命名規(guī)范,無宏文本。
定義空宏是為了配合條件編譯指令使用。下面將做介紹,已經(jīng)定義的有參宏、無參宏、空宏,都是用宏刪除指令(#undef)刪除。
#undef 宏明//宏名就是指定將被刪除的宏
刪除宏以后,宏就不能再繼續(xù)使用了。但可以再次被定義
2.3 條件編譯指令
程序開發(fā)過程中,源程序可能有多個版本,例如不同語種的版本,或者是調(diào)試版本和發(fā)行版本,如果用不同的程序文件存放不同版本的源代碼,文件數(shù)量將迅速增加,也容易導致代碼修改時的不一致問題,條件編譯指令運行程序員將不同版本的源代碼寫在同一程序文件中,便于管理維護修改,條件編譯指令,有2種格式。
編譯預處理指令語法:條件編譯指令(格式1)
#ifdef 空宏名
代碼段1
#else
代碼段2
#endif
- 編譯器在編譯這段代碼時,如果指定的空宏已定義,則編譯代碼段1,否則編譯代碼段2
- 如果沒有代碼段2,則可以省略#else
#include <iostream>
using namespace std;
#define PI 3.14159 //為pi定義一個符號常量
#define ENGLISH_VERSION //定義一個空宏ENGLISH_VERSION,將這條語句注釋調(diào),此程序就會編譯成一個中文版程序。
int main()
{
#ifdef ENGLISH_VERSION //如果定義了ENGLISH_VERSION,則編譯以下代碼
cout<<"Please input a radius";
#else //否則編譯以下代碼
cout<<"請輸入圓的半徑";
#endif
/* 以下2條指令是中英文版公用的指令,故無需指定條件編譯*/
double r;
cin >>r;
#ifdef ENGLISH_VERSION
cout<<"Area:"<<PI*r*r<<endl;
cout<<"perimeter"<<PI*2*r<<endl;
#else
cout<<"面積為:"<<PI*r*r<<endl;
cout<<"周長為"<<PI*2*r<<endl;
#endif
return 0;
}
編譯預處理指令語法:條件編譯指令(格式2)
#ifdef 常量表達式
代碼段1
#else
代碼段2
#endif
- 編譯器在編譯這段代碼時,如果指定的常量表達式結(jié)果不為0,則編譯代碼段1,否則編譯代碼段2,常量表達式只能包含常量或符號常量
- 如果沒有代碼段2,則可以省略#else
#include <iostream>
using namespace std;
#define PI 3.14159 //為pi定義一個符號常量
#define ENGLISH_VERSION 1 //定義一個符號常量ENGLISH_VERSION,值為1,則編譯為英文版,當值設(shè)為0,將編譯中文版。
int main()
{
#ifdef ENGLISH_VERSION //如果ENGLISH_VERSION的值不為0,則編譯以下代碼
cout<<"Please input a radius";
#else //否則編譯以下代碼
cout<<"請輸入圓的半徑";
#endif
/* 以下2條指令是中英文版公用的指令,故無需指定條件編譯*/
double r;
cin >>r;#ifdef ENGLISH_VERSION
cout<<"Area:"<<PI*r*r<<endl;
cout<<"perimeter"<<PI*2*r<<endl;
#else
cout<<"面積為:"<<PI*r*r<<endl;
cout<<"周長為"<<PI*2*r<<endl;
#endif
return 0;
}
三、幾種特殊形式的函數(shù)
3.1 帶默認形參的函數(shù)
- 定義函數(shù)或聲明函數(shù)原型時可以指定形式參數(shù)的默認值,這就是帶默認形參值的函數(shù)
- 調(diào)用帶默認形參值得函數(shù)時,如果給出實參值,則將實參值賦給形參變量,如果沒有,則將默認值賦給形參變量。
例子:人民幣匯率轉(zhuǎn)換表(2015年6月19日)
| 外幣(數(shù)量:1) | 人民幣(單位:元) |
|---|---|
| 美元 | 6.1104 |
| 歐元 | 6.9698 |
| 英鎊 | 9.7347 |
| 港幣 | 0.78817 |
#include <iostream>
using namespace std;
double Exchange(double amount=100,double rate=6.1104) //給定匯率默認形參值為美元的匯率
{
return amount/rate;
}
int main()
{
int x;
cout <<"請輸入人民數(shù)量,單位元:";
cin >> x;
cout<<x<<"元人民幣="<<Exchange(x)<<"美元"<<endl; //人民幣>>美元,計算美元時,不需要傳遞匯率
cout<<x<<"元人民幣="<<Exchange(x,6.9698)<<"歐元"<<endl; //人民幣>>歐元
cout<<x<<"元人民幣="<<Exchange(x,9.7347)<<"英鎊"<<endl; //人民幣>>英鎊
cout<<x<<"元人民幣="<<Exchange(x,0.78817)<<"港幣"<<endl; //人民幣>>港幣
return 0;
}
帶默認形參值函數(shù)的語法細則:
- 帶默認值的形參:調(diào)用時如果給出實參值,則將實參值賦值給形參變量,如果沒有,則將默認值賦值給形參變量,不帶默認值的形參在調(diào)用時必須給出實參,否則屬于語法錯誤。
- 在函數(shù)原型聲明中指定默認值:如果函數(shù)定義在調(diào)用語句之后,應該在調(diào)用語句之前對函數(shù)原型進行聲明??梢栽诼暶髡Z句中指定形式參數(shù)的默認值,此時函數(shù)定義中不能再指定默認值。函數(shù)具有文件作用域,同一函數(shù)在相同的作用域中只能指定一次默認值。
- 如果函數(shù)定義在其他文件中,應該在調(diào)用語句之前聲明外部函數(shù)的原型,可以在聲明語句中指定形式參數(shù)在本文件中的默認值,并且可以與原函數(shù)定義中的默認值不同。
- 同一函數(shù)在不同的作用域中可以指定不同的默認值。如果多個默認值同時有效,調(diào)用函數(shù)時根據(jù)局部優(yōu)先原則選擇默認值。
演示程序:在不同作用域為函數(shù)形參指定默認值
#include <iostream>
using namespace std;
void fun(int p=10);//在申明函數(shù)fun時,指定文件作用域的形參p的默認值為10
int main()
{
fun(); //使用文件作用域的默認值,函數(shù)fun的顯示結(jié)果為10
{
void fun(int p=20);//再次申明函數(shù)fun,將具有塊作用域的形參p的默認值為20
fun(); //使用塊作用域(局部優(yōu)先),fun的顯示結(jié)果為20
}
return 0;
}
void fun(int p) //在申明函數(shù)時,已經(jīng)指定了p的默認值,此時不能再指定
{
cout<<p<<endl; //顯示形參p接收到的實參值
}
帶默認值的形參,必須定義在形參列表的后面,形參列表中,可能有的形參帶默認值,有的不帶,定義函數(shù)或聲明函數(shù)原型時,必須把帶默認值的形參放在不帶默認值的形參的后面。
3.2 重載函數(shù)
調(diào)用函數(shù)時通過函數(shù)名指定調(diào)用哪個函數(shù),函數(shù)名是函數(shù)的標識,通常,同一文件之中的函數(shù)不能重名,不文件中的非靜態(tài)函數(shù)(外部函數(shù))之間也不能重名,
c++語言規(guī)定,如果兩個函數(shù)的形參個數(shù)不同或數(shù)據(jù)類型不同,那么這兩個函數(shù)就可以重名,重名的函數(shù)被稱為重載函數(shù)。
將兩個或兩個以上函數(shù)定義為重載函數(shù)的原因,是這些函數(shù)的功能相同或相近,使用相同的名字方便碼農(nóng)記憶,也不用絞盡腦子去想如何起不同的名字
#include <iostream>
using namespace std;
// Max為重載函數(shù)名,功能是求最大值。
int Max(int x,int y){return ((x>y)?x:y);}
//double型浮點類型,為64位寬度,float型為32位寬度
double Max(double x,double y){return ((x>y)?x:y);}
int Max(int x, int y, int z)
{
int m;
m = (x>y)?x:y;
m = (x>z)?m:z;
return m;
}
cout<<Max(9,5); //自動調(diào)用:int Max(int x,int y)
cout<<Max(9.0,5.0); //自動調(diào)用:double Max(double x,double y)
cout<<Max(9,5,17); //自動調(diào)用:int Max(int x, int y, int z)
C++在編譯語言時,由編譯器根據(jù)調(diào)用語句中實參的個數(shù)和類型,來自動調(diào)用形參最匹配的那個重載函數(shù),簡而言之就是通過形參和實參的匹配原則來調(diào)用重載函數(shù)。
注意:在應用重載函數(shù)時,如果兩個函數(shù)僅僅是返回值類型不同或形參名不同,那么不能講這兩個函數(shù)命名為重載函數(shù),否則會出現(xiàn)語法錯誤。也不能將兩個功能差異很大的函數(shù)命名為重載函數(shù),雖然沒有語法錯誤,但是會在運用中給程序員造成混淆。
3.3 內(nèi)聯(lián)函數(shù)
調(diào)用函數(shù):函數(shù)跳轉(zhuǎn)和數(shù)據(jù)傳遞需要執(zhí)行一些額外的操作,實現(xiàn)相同的功能,單一主函數(shù)程序比主函數(shù)+子函數(shù)程序執(zhí)行速度更快,及函數(shù)跳轉(zhuǎn)會降低程序的執(zhí)行效率,但函數(shù)是團隊分工協(xié)作和代碼重用的基礎(chǔ),函數(shù)能夠提高程序的開發(fā)效率。
內(nèi)聯(lián)函數(shù):內(nèi)聯(lián)函數(shù)是一種特殊的函數(shù),它在保障程序開發(fā)效率的同時,不會減低程序的執(zhí)行效率。
其原理是:編譯源程序時將函數(shù)代碼直接嵌入到每一個調(diào)用語句處,而在執(zhí)行時不再進行函數(shù)跳轉(zhuǎn)和數(shù)據(jù)傳遞。
#include <iostream>
using namespace std;
//使用inline關(guān)鍵字申明內(nèi)聯(lián)函數(shù)
inline double Exchange(double amount,double rate=6.1104){ return amount / rate; }
int main()
{
int x;
cout <<"請輸入人民數(shù)量,單位元:";
cin >> x;
cout<<x<<"元人民幣="<<Exchange(x)<<"美元"<<endl; //人民幣>>美元
cout<<x<<"元人民幣="<<Exchange(x,6.9698)<<"歐元"<<endl; //人民幣>>歐元
cout<<x<<"元人民幣="<<Exchange(x,9.7347)<<"英鎊"<<endl; //人民幣>>英鎊
cout<<x<<"元人民幣="<<Exchange(x,0.78817)<<"港幣"<<endl; //人民幣>>港幣
return 0;
}
內(nèi)聯(lián)函數(shù)需要在被調(diào)用的函數(shù)前加上inline關(guān)鍵值進行申明。
內(nèi)聯(lián)函數(shù)需要是簡單的函數(shù),編譯器不能保證程序員所定義或申明的內(nèi)聯(lián)函數(shù)最終都能按照內(nèi)聯(lián)的方式進行編譯,如果該函數(shù)的函數(shù)體比較復雜,比如有循環(huán)語句,那么編譯器將自動按照非內(nèi)聯(lián)函數(shù)的方式進行編譯。
內(nèi)聯(lián)函數(shù)只有被多次調(diào)用,其執(zhí)行效率才能體現(xiàn)出來,因此一般只是將頻繁調(diào)用的簡單函數(shù),定義為內(nèi)聯(lián)函數(shù)。
3.4 帶形參和返回值的主函數(shù)
計算機系統(tǒng)包括硬件和軟件2部分,操作系統(tǒng)是計算機中最基礎(chǔ)最重要的軟件,操作系統(tǒng)直接運行于硬件之上,啟動計算機后,計算機自動加載執(zhí)行,只有在操作系統(tǒng)運行之后,其他軟件才能運行,用戶執(zhí)行某個程序,其實是向操作系統(tǒng)下達了一個執(zhí)行程序的指令。在命令行操作系統(tǒng)中,如果要執(zhí)行某個程序,就可以下達類似于:test.exe或test指令,然后回車,操作系統(tǒng)接收該指令,然后將程序文件讀入內(nèi)存,找到該程序的主函數(shù)main,從主函的第一條指令開始執(zhí)行,程序執(zhí)行結(jié)束后,從主函數(shù)main返回操作系統(tǒng)。
可以將操作系統(tǒng)執(zhí)行某個程序的過程,理解成操作系統(tǒng)調(diào)用該程序主函數(shù)main 的過程。
用戶在向操作系統(tǒng)下的程序執(zhí)行指令的同時,可以傳遞某些原始數(shù)據(jù)。也就是說,一個程序的主函數(shù)main可以定義形參,接收數(shù)據(jù),可以定義返回值,將自己的運行狀態(tài)返回給操作系統(tǒng)。
c++程序中的主函數(shù)main()的語法細則:
- 一個c++程序必須有并取只有1個名為main的主函數(shù)。
- 主函數(shù)是程序執(zhí)行的起點
- 主函數(shù)不能被重載
- 主函數(shù)的類型應該為int型,需要返回一個int型整數(shù)
- 主函數(shù)可以
- 定義形參來接收實參數(shù)據(jù)
- 也可以省略形參(此時操作系統(tǒng)所傳遞過來的實參數(shù)據(jù)將被忽略)
定義有形參的主函數(shù)main:
int main(int argc, char*argv[])
{
/*
此處定義代碼語句
*/
return 0;
}
語法說明:
- argc標識所接收到的參數(shù)個數(shù);
- argv是一個char型指針數(shù)組,數(shù)組元素分別為argv[0]~arbv[argc-1]。參數(shù)以字符串形式傳遞,其中argc[0]所指向的字符數(shù)組存放的是該文件程序的文件名,argc[1]所指向的字符數(shù)組存放的是第1個實參數(shù)據(jù),......;
- 采用無參形式時,操作系統(tǒng)所傳遞過來的實參數(shù)據(jù)將被忽略
- 主函數(shù)通過返回值將自己的運轉(zhuǎn)狀態(tài)返回給操作系統(tǒng),通常0表示正常,用-1表示異常。
代碼示例:帶形參和返回值的主函數(shù)定義格式
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
for(int n=0; n<argc; n++) //接收用戶輸入的參數(shù),使用循環(huán)語句
cout<<argv[n]<<endl; //顯示用戶輸入的參數(shù)。
return 0;
}
Microsoft c++編譯器對主函數(shù)語法處理的差異
美國微軟公司開發(fā)的c++編譯器,在對c++語法的處理上有一定的差異,主要提現(xiàn)在visual c++ 6.0和visual studio系列這兩個集成開發(fā)環(huán)境的使用上。
-
visual c++ 6.0中,主函數(shù)可以沒有返回值,也就是主函數(shù)可以定義為void,例如:
#include <iostream> using namespace std; void main(){ //此處定義函數(shù)體代碼 return; //如果該語句是最后一條語句,則可以省略 } -
visual studio 系列集成開發(fā)環(huán)境
該環(huán)境在主函數(shù)的定義上,于其他環(huán)境有所區(qū)別,該系列可以使用應用程序向?qū)硇陆ㄩ_發(fā)項目,例如:假設(shè)我們新建win32控制臺應用程序,該環(huán)境將自動創(chuàng)建一個c++源文件,該文件的文件頭和主函數(shù)如下:
#include "stdafx.h" //包含visualstudio集成開發(fā)環(huán)境所必須的一些信息。 int _tmain(int argc, _TCHAR *argv[]) { system("pause");//添加該語句,暫停程序執(zhí)行,以便程序員檢查運行結(jié)果。 return 0; }這是因為visual studio集成開發(fā)環(huán)境同時支持ANSI編碼和Unicode編碼的程序開發(fā),使用_tmain()命名主函數(shù),可以很方便的在兩種字符代碼之間切換。
初學者使用visual studio時,可以選擇空項目,然后自己添加新建源程序文件,在return語句之前,記得加入一個system("pause");添加該語句,暫停程序執(zhí)行,以便程序員檢查運行結(jié)果。
-
使用Windows圖形界面程序時
需要將主函數(shù)定義為WinMain()或_tWinMain(),這個時windows運用程序執(zhí)行的起點。這兩個名字是微軟公司為Windows圖形界面的主函數(shù)所指定的函數(shù)名。并不是c++標準指定的。
3.6遞歸函數(shù)
假設(shè)要求一個數(shù)N的階乘,也就是求N!
1、遞推法:
遞推法利用已知條件(0!= 1)和地推公式:
n!= n x(n-1)
逐步遞推求出1!、2!、... ...、直到求出N!
#include <iostream>
using namespace std;
int Factorial(int N)
{
int result = 1; //已知:0!=1
for(int n = 1; n<=N;n++)
result = n*result; //遞推公式:n!= n*(n-1)
return result;
}
int main()
{
int N;
cout<<"請輸入原始值N:";
cin>>N;
cout<<Factorial(N);
return 0;
}
遞推法求解問題的基本思想是從已知條件出發(fā),根據(jù)地推公司由簡到繁,逐步逼近,最終求出問題的解,這種方式也稱為正向遞推。正向遞推的每一步驟都是已知問題n-1的解,遞推求解問題n的解,這些遞推步驟是在重復計算遞推公式,可使用循環(huán)結(jié)構(gòu)來描述遞推算法。
2、遞歸法:
遞歸法是程序設(shè)計中一種基于“函數(shù)嵌套調(diào)用”原理求解問題的方法。遞歸法求解問題的過程分2步完成:
-
第一步
按照遞推公式由繁到簡,將求問題n的解降階成求問題n-1的解,直到滿足已知條件(稱為遞歸終結(jié)條件)不能降階為止,這個過程稱為逆向遞推;
-
第二步
函數(shù)逐級返回結(jié)果,最終求出問題的解,這個過程稱為回歸。
#include <iostream>
using namespace std;
int Factorial(int N)
{
int result; //用于保存結(jié)果
if(N==0)
result=0;
else if(N==1)
result = 1;
else
result = N*Factorial(N-1); //調(diào)用自身,參數(shù)降階為N-1
return result;
}
int main()
{
int N;
cout<<"請輸入原始值N:";
cin>>N;
cout<<Factorial(N);
return 0;
}
c++語言將直接或間接調(diào)用自身的函數(shù),稱為遞歸函數(shù)。
與前面所講解的重載函數(shù),帶默認形參值、內(nèi)聯(lián)函數(shù)的函數(shù)不同的是,遞歸函數(shù)不僅僅是一個語法概念,其背后還暗含了遞歸法求解問題的算法設(shè)計思想。
遞歸函數(shù)的定義和調(diào)用:
函數(shù)類型 函數(shù)名(形式參數(shù)列表)
{
... ...
if(遞歸終結(jié)條件) //條件一
取得已知結(jié)果 //條件二
else
按照遞歸公式調(diào)用自身 //條件三
... ...
}
遞歸函數(shù)的執(zhí)行過程:
- 計算機在執(zhí)行函數(shù)調(diào)用語句跳轉(zhuǎn)到被調(diào)函數(shù)時,為其形參及函數(shù)體中定義的局部變量分配內(nèi)存,建立被調(diào)函數(shù)的棧幀
- 函數(shù)可以嵌套調(diào)用,每增加一級函數(shù)調(diào)用,棧幀就增加一個,每退出一級函數(shù)調(diào)用,棧幀就減少一個
- 計算機執(zhí)行遞歸函數(shù)的過程就是遞歸函數(shù)不斷嵌套調(diào)用自身、不斷建立新棧幀的過程,即逆向遞推的過程。
- 當遞歸終結(jié)條件成立時停止調(diào)用,開始逐級返回結(jié)果,退出遞歸函數(shù)并依次釋放棧幀,這就是回歸的過程。
遞歸的函數(shù)嵌套調(diào)用,次數(shù)必須是有限的,無線調(diào)用,會不停建立新的棧幀,最終超出程序的內(nèi)存占用,會導致棧溢出的錯誤。
-
遞歸法比遞推法速度慢。
遞推算法使用循環(huán)算法來實現(xiàn),速度塊,遞歸算法,使用函數(shù)調(diào)用,需要額外的操作, 因此速度慢
-
遞歸法比遞推法適用范圍廣
能用遞推求解的算法,一定可以使用遞歸來求解,反之則不然。
漢諾塔問題:
#include <iostream>
using namespace std;
void hanoi(int N,char source, char relay, char destination)
{
if(N==1)
cout<<source<<"-->>"<<destination<<endl;
else
{
/*hanoi(N-1,source=a,destination=b,relay=c)把a上的N-1個盤中移到b*/
hanoi(N-1, source, destination, relay);
cout<<source<<"-->>"<<destination<<endl;// a-->>b
/*hanoi(N-1,relay=a, source=b, destination=c)把b上的N-1個移動到c*/
hanoi(N-1, relay, source, destination);
}
}
int main()
{
int N;
cout<<"請輸入要移動的圓盤數(shù)量N:";
cin>>N;
cout<<"移動"<<N<<"個盤子的步驟"<<endl;
hanoi(N,'a','b','c');
return 0;
}
四、系統(tǒng)函數(shù)
程序員編寫的函數(shù)可以在下個項目中繼續(xù)使用,這就是重用函數(shù)的代碼,隨著時間的推移,程序員將積累越來越多的函數(shù),重用這些函數(shù),可以顯著的提高開發(fā)效率
c++也預先編寫了很多常用的函數(shù),提供給廣大程序員使用,這些函數(shù)統(tǒng)稱為系統(tǒng)函數(shù)
c++語言是在C語言基礎(chǔ)上發(fā)展而來的,C語言是機構(gòu)化程序設(shè)計語言,系統(tǒng)函數(shù)是其重要的附屬組成部分。C語言函數(shù)的源代碼,被編譯成機器語言,以庫文件的形式,隨編譯器系統(tǒng)提供。庫文件的名稱,通常為”.lib“,庫文件通常存在編譯器安裝文件目錄中的lib子目錄中,這些庫文件被稱為標準C庫。
調(diào)用標準C庫中的系統(tǒng)函數(shù),都需要先聲明其函數(shù)原型,為了方便程序員,C語言預先編寫好這些系統(tǒng)函數(shù)的原型聲明語句,并按功能分別保存在若干個不同的頭文件中。程序員只要用#include指令,包含相應的頭文件,就可以調(diào)用這些系統(tǒng)函數(shù)。程序鏈接時,被調(diào)用的系統(tǒng)函數(shù)代碼將被連接到可執(zhí)行程序文件之中。
c++語言全盤繼承了C語言的標準C庫,另外又增加了一些新的庫,新庫中包括了新增的系統(tǒng)函數(shù),但更對的是新增了面向?qū)ο蟮南到y(tǒng)類庫,這些新庫,就被稱為c++標準庫。c++標準庫引入了命名空間的概念,所有的新增的系統(tǒng)函數(shù)和系統(tǒng)類庫,都定義在命名空間std當中。
4.1 C語言系統(tǒng)函數(shù)
系統(tǒng)函數(shù)極大的擴展了C語言的功能,程序員能夠站在更高的起點上開發(fā)程序,程序員在調(diào)用系統(tǒng)函數(shù)前,需閱讀系統(tǒng)提供的手冊,學習各系統(tǒng)函數(shù)的功能及調(diào)用語法,并用#include指令包含相應的頭文件。標準C庫的頭文件的擴展名都是“.h”
1 、輸入輸出函數(shù)
C語言本身沒有輸入輸出語句,而是通過輸入輸出函數(shù)來實現(xiàn)輸入和輸出,下面介紹兩個標準的輸入函數(shù)以及2個標準的輸出函數(shù)。使用這些輸入輸出函數(shù),需要使用#include指令包含頭文件<stdio.h>
格式化輸入函數(shù):scanf
int scanf(char *format, 變量地址列表);
- 參數(shù)format是格式控制字符串,其中包括格式符和分隔符,格式符是以”%“開頭的字符串,用于指定輸入數(shù)據(jù)的類型或格式,參見表6-2
- 變量地址列表指定保存輸入數(shù)據(jù)的變量地址,一次可為多個變量輸入數(shù)據(jù),此時應為每個變量指定一個格式符,格式符應與變量的數(shù)據(jù)類型一致,多個變量地址之間用逗號”,“隔開,多個格式符之間通常用空格或逗號隔開,輸入數(shù)據(jù)時相應地也用空格或逗號隔開,以回車結(jié)束。
- 返回值是int型,返回輸入數(shù)據(jù)的個數(shù)。
- 調(diào)用函數(shù)時,程序暫停執(zhí)行,等待用戶從鍵盤輸入數(shù)據(jù),以回車結(jié)束。
#include <stdio.h>
int x; scanf("%d",&x); //輸入十進制整數(shù),保存到int型變量x中
float y; scanf("%f",&y ); //輸入十進制實數(shù),保存到float型變量y中
double z; scanf("%lf",&z);//輸入十進制實數(shù),保存到double型變量z中
char ch; scanf("%c",&ch);//輸入一個字符,保存在char型變量ch中
char str[20]; scanf("%s",str)//輸入一個字符串,保存到char型數(shù)組str中
scanf("%d %f %lf %c %s",&x,&y,&z,&ch,&str)//一次性輸入5個不同類型的數(shù)據(jù)
表6-2:
| 整數(shù) | %d | 十進制數(shù) |
|---|---|---|
| 整數(shù) | %o | 八進制數(shù) |
| 整數(shù) | %x | 十六進制數(shù) |
| 實數(shù) | %f | float型浮點數(shù) |
| 實數(shù) | %lf | double型浮點數(shù) |
| - | %c | 單個字符 |
| - | %s | 字符串 |
格式化輸出函數(shù)printf:
int printf(char*format,表達式列表);
- 參數(shù)format是格式化控制字符串,其中包括格式符和非格式符,格式符是以“%”開頭,用于指定輸出數(shù)據(jù)的類型或格式,參見表6-2,非格式符原樣輸出
- printf中的格式符可以指定輸出數(shù)據(jù)的域?qū)挘达@示時的占位寬度),實際數(shù)據(jù)達不到域?qū)挄r補空格,輸出實數(shù)時還可以指定輸出的精度(即輸出幾位小數(shù))
- 表達式列表指定需要輸出的常量、變量表達式,一次性可以輸出多個表達式,此時應為每個表達式指定一個格式符,格式符應與表達式結(jié)果的數(shù)據(jù)類型一致,多個表達式之間用逗號“,”隔開
- 返回值是int型,返回輸出數(shù)據(jù)的個數(shù)
- 調(diào)用該函數(shù)時將從右到左的順序計算各表達式,然后按從左至右的順序顯示各表達式的結(jié)果。
舉例:
int x=10; printf("x+5=%d",x+5); //輸出x+5=15
float y=5.5; printf("y+1=%f",y+1);//輸出y+1=6.5
double z=5.5; printf("z=%lf",z);//輸出z=5.5
char ch='A';printf("ch=%c",ch);//輸出ch=A
char str[20]="china";printf("%s",str);//輸出china
printf("%5d, %5.2f, %5.2lf, %5c, %5s",x, y, z, ch, str);//一次顯示5各不同類型的數(shù)據(jù)//格式花每個數(shù)據(jù)的輸出寬度為5,輸出實數(shù)時保留2位小數(shù)。
單字符輸入函數(shù)getchar
語法說明:
int getchar();
- 調(diào)用該函數(shù)時,程序暫停執(zhí)行,等待用戶從鍵盤輸入一個字符串,以回車結(jié)束
- 返回值是int型,返回所輸入字符的ASCII編碼值
char ch;
ch = getchar();//輸入一個字符,保存到變量ch中
單字符輸出函數(shù)putchar
int putchar(int c);
- 調(diào)用該函數(shù)將變量c中的字符輸出到顯示器上,實際上,變量c中保存的式字符的ASCII碼值,
- 返回值是int型,返回所輸出的字符的ASCII碼值
putchar('A');//顯示字符A
putchar('A'+32);//顯示字符A
4.2 數(shù)學函數(shù):#include <math.h>
求一元二次方程根的演示程序
首先要判斷,則
,否則x無實數(shù)根
#include <stdio.h>
#include <math.h>
#include <iostream>
int main()
{
double a, b, c; //一元二次方程的實數(shù)部分。
scanf("%lf %lf %lf",&a,&b,&c); //輸入這三個實數(shù)
double d = b * b - 4 * a * c; //計算Δ的值
if (d < 0) printf("無實數(shù)根\n");
else
{
double x1, x2;
x1 = (-b + sqrt(d)) / (2 * a); x2 = (-b - sqrt(d)) / (2 * a);
printf("x1=%.10lf,x2=%.10lf\n", x1, x2);
}
system("pause");
return 0;
}
4.3 字符串處理函數(shù)
#include <stdio.h>
#include <string.h>
#include <iostream>
int main()
{
char str1[20]="hello",str2[10];
printf("%d\n",strlen(str1));// 函數(shù)strlen求字符數(shù)組str1中字符串的長度,結(jié)果為5
strcpy(str2,",world!");//函數(shù)strcpy將字符串“,world!"拷貝到字符數(shù)組str2中
strcat(str1,str2);//函數(shù)strcat將str2中的字符串連接到str1中字符串的后面
printf("%s\n",str1);//顯示連接后的str1字符串,結(jié)果為hello world!
printf("%d\n",strlen(str1));//顯示連接后str1中新字符串的長度,結(jié)果為13
system("pause");
return 0;
}
4.4動態(tài)內(nèi)存分配函數(shù)
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
int main()
{
int *p;
/* 函數(shù)malloc返回所分配內(nèi)存的首地址,且必須制定分配的內(nèi)存單元的大小,返回值其類型為“void”,
需轉(zhuǎn)為“int*”,而10*sizeof(int)可以計算出,1個int型變量所占用的字節(jié)數(shù)。 */
p = (int *)malloc(10*sizeof(int));//動態(tài)分配一個含有10個元素的int型數(shù)組
for(int n=0;n<10;n++)
{
*(p+n) = n*n;
printf("%d\n",p[n]);
}
free(p);
system("pause");
return 0;
}
4.5 命名空間
我們在編寫程序時,可以使用各種渠道得到的函數(shù),但是C語言和C++都規(guī)定,所有的外部函數(shù)不能重名,但我們在使用外來函數(shù)時,不同的機構(gòu)不同的程序員開發(fā)的函數(shù)難免會重名,比如山東的德州和美國的德州,單獨看德州兩個字,這兩個地方重名了,但是如果加上國度,這兩個地方就能夠準確的區(qū)分開來,這個國度就是命名空間的概念。
C++引入了命名空間的概念,不同的程序員在各自的命名空間內(nèi)定義外部函數(shù)和全局變量,就可以消除重名的問題。
1、定義方式
在命名空間中定義函數(shù)和全局變量,使用namespace關(guān)鍵值進行申明。然后將函數(shù)和全局變量定義在其后的一對大括號{}之中。例子如下:
namespace Tom // 通過namespace進行申明一個名叫Tom的命名空間
{
int x,y;
void fun1(){......}
void fun2(){......}
}
2、訪問方式
訪問命名空間中的函數(shù)和全局變量
1、直接訪問
通過命名空間名稱+雙冒號::+全局變量標識符/函數(shù)名的方式進行訪問:
Tom::x=10; Tom::y=20; //訪問全局變量
Tom::fun1(); Tom::fun2(); //調(diào)用命名空間Tom里的函數(shù)
雙冒號(::)稱之為作用域運算符
2、使用關(guān)鍵字using先申明各個標識符的命名空間
也可以先聲明各個標識符的命名空間,使用關(guān)鍵字using,如果事先聲明了要訪問的各個標識符,再訪問的時候,可以省略命名空間名。
using Tom::x; //先聲明各標識符的命名空間
using Tom::y;
using Tom::fun1();
using Tom::fun2();
x = 10; y = 20;
//再通過標識符訪問,訪問時可以省略命名空間
fun1(); fun2();
3、統(tǒng)以申明命名空間的所有標識符
也可以先統(tǒng)一聲明命名空間里的所有標識符,再通過標識符直接訪問。
using namespace Tom; //先統(tǒng)一聲明命名空間Tom里的所有標識符
x = 10; y = 20;
//再通過標識符訪問,訪問時可以省略命名空間
fun1(); fun2();
通過這種方式訪問方式是最簡單直接的。
4.6 匿名命名空間
using 和 namespace都是C++語言保留的關(guān)鍵字,C++語言還有個默認的匿名的命名空間,如果程序員編寫函數(shù)或全局變量,定義時沒有定義在任何命名空間里,則默認就屬于該匿名命名空間,C++標準庫就引入了命名空間的概念,所有函數(shù)實體,例如外部函數(shù)、全局變量或?qū)ο蟮?,都定義再命名空間std當中,使用這些函數(shù)實體,除了需要使用#include指令包含相應的頭文件之外,還需要聲明其命名空間,例如需要使用cin>>和cout<<對象輸入輸出數(shù)據(jù),則除了需要用#include指令包含頭文件<iostream>之外,還需要聲明其命名空間std,就是using namespace std;的由來。
4.7 c++中的系統(tǒng)函數(shù)
c++語言全盤繼承了c語言的語法規(guī)則,同時也全盤繼承了C語言的標準C庫。c++標準庫新增了一些系統(tǒng)函數(shù),更多是新增了面向?qū)ο蟪绦蛟O(shè)計的系統(tǒng)類庫
1、c++語言系統(tǒng)類庫
經(jīng)常使用的cin,cout輸入輸出語句,實際上就是c++標準庫預先定義好的對象,cin是c++標準庫定義的一個標準輸入流對象,而cout則是一個標準輸出流對象,使用cin和cout對象使用#include指令包含相應的文件頭“iostream”,還需要聲明其命名空間“std”
#include <iostream>using namespace std;
c++語言是在c語言基礎(chǔ)上發(fā)展而來的,可以在很大程度上替代c語言,例如
- 用cin,cout輸入/輸出流對象,代替原來的輸入輸出函數(shù)
- 用字符串類string,代替原來的字符數(shù)組和字符串處理函數(shù)
- 用new和delete這2個運算符,代替原來的動態(tài)內(nèi)存分配函數(shù):malloc和free
C語言是機構(gòu)化設(shè)計語言,它使用函數(shù)來提很多常使用的功能,c++語言繼承了這些機構(gòu)化程序設(shè)計方法,但又新增了面向?qū)ο蟪绦蛟O(shè)計方法,就是使用類和對象來實現(xiàn)程序功能。
4.8 多文件結(jié)構(gòu)下程序員與函數(shù)的關(guān)系
-
為別人提供函數(shù)的程序員
- 將常用功能或算法定義成函數(shù),保存到源文件(擴展名為.cpp);將源程序文件編譯成目標代碼文件(擴展名為.obj);通常還會進一步將目標代碼打包成一個函數(shù)庫文件(擴展名通常為.lib)
- 為函數(shù)庫中的函數(shù)編寫聲明語句,集中保存在一個頭文件中(擴展名為.h)
- 發(fā)布或小數(shù)函數(shù)庫產(chǎn)品,其中包含庫文件和頭文件,庫文件為目標代碼(即機器語言),其他程序員只能調(diào)用,無法閱讀修改,而頭文件是函數(shù)聲明語句的源碼,其它程序員可以閱讀,以了解函數(shù)的功能與調(diào)用接口
-
使用別人函數(shù)的程序員
- 可以使用系統(tǒng)函數(shù),也可以獲得(比如購買)第三方開發(fā)的函數(shù)庫
- 編寫程序時調(diào)用別人函數(shù)庫中的函數(shù),以實現(xiàn)某種特定的功能,調(diào)用前需要用#include指令包含相應的文件頭
- 連接時,連接器將函數(shù)庫中被調(diào)函數(shù)代碼與自己的程序連接到一起,形成最終語句。
五、結(jié)構(gòu)化程序設(shè)計回顧
運用結(jié)構(gòu)化程序設(shè)計,可以將一個大型功能,分解成多個模塊,分而治之,c++語言以函數(shù)的形式來描述各個模塊,這就是函數(shù)的定義。然后再通過調(diào)用,將各個算法模塊組裝起來,最終形成一個完整的算法流程。
一個編寫好的c++函數(shù)可以備同一項目的多個程序員調(diào)用,也可以在今后的項目中繼續(xù)使用,這就是函數(shù)代碼的重用。
市場上還要很多廠家為程序員提供編寫好的,可以實習的各種不同功能得函數(shù)庫,本章將以Windows圖形用戶界面GUI程序,win32 AP1函數(shù)庫進行講解如何開發(fā)一個windows圖形界面程序
Windows圖形用戶界面程序編程原理
- 使用圖形界面設(shè)計器設(shè)計圖形界面窗口,設(shè)計好的窗口備稱為資源。
- 編寫主函數(shù),創(chuàng)建并顯示窗口。
- Windows系統(tǒng)負責監(jiān)控鼠標并捕獲用戶操作,如果用戶對窗口進行了操作(例如點擊了某個按鈕),那么Windows操作系統(tǒng)自動調(diào)用該窗口對應的處理函數(shù)(被稱為窗口過程)
- 調(diào)用時會以消息的形式傳遞參數(shù),消息參數(shù)用于區(qū)分用戶進行了何種操作,消息參數(shù)以整數(shù)來標記用戶所做的不同操作,為了方便程序員,win32 API將這些編號定義成易于記憶的符號常量,例如WM_COMMAND(表示點擊了某個按鈕或菜單)、WM_CLOSED(點擊了關(guān)閉窗口的x號)等。
windows操作系統(tǒng)調(diào)用某個窗口過程,并傳遞消息參數(shù),這邊稱為向該窗口過程發(fā)送消息;窗口過程接收消息,然后進行處理,這被稱為對消息的響應。
Windows圖形用戶界面運行方式:
-
用戶啟動程序
主函數(shù)負責創(chuàng)建程序窗口,主函數(shù)是由程序員編寫語句定義的
-
用戶操作界面
Windows將捕捉鼠標操作,調(diào)用窗口過程函數(shù),并傳遞WM_COMMAND消息,窗口過程不是程序員在主函數(shù)中編寫函數(shù)調(diào)用語句來調(diào)用的,而是由Windows操作系統(tǒng)自動調(diào)用的。像窗口過程這樣由Windows操作系統(tǒng)調(diào)用的函數(shù),稱為回調(diào)函數(shù)。
窗口過程函數(shù),根據(jù)接收到的消息參數(shù),可以判斷出用戶進行了何種操作,并按照程序功能執(zhí)行相應的算法并顯示結(jié)果。因此程序員需要在窗口操作過程中編寫功能實現(xiàn)的語句。
項目創(chuàng)建方法:
- 新建一個win32 Application工程
- 使用圖形界面設(shè)計器來設(shè)計窗口界面
- 新建一個c++源程序文件,在其中編寫對應的完整的c++程序代碼。
六、自定義數(shù)據(jù)類型
c++語言提供了比較完善的基本數(shù)據(jù)類型,其中包括整型(int、short、long)、浮點型(float、double)、字符型(char)和布爾類型(bool)等4大類
程序員可以根據(jù)需要為這些基本數(shù)據(jù)類型重新命名一個別名,或基于這些基本數(shù)據(jù)定義新的復雜數(shù)據(jù)類型,這些類型被統(tǒng)稱為自定義數(shù)據(jù)類型,可以使用自定義數(shù)據(jù)類型來定義變量。
本節(jié)將介紹“typedef”類型定義語句,以及枚舉、結(jié)構(gòu)體、聯(lián)合體等常用自定義數(shù)據(jù)類型的定義和使用方法。
6.1 類型定義typedef
可以使用typedef關(guān)鍵字為基本數(shù)據(jù)類型,命名一個別名,或者為指針,數(shù)組定義新的數(shù)據(jù)類型
為基本數(shù)據(jù)類型命名一個別名:
typedef unsigned char AGE;
AGE X; //等價于unsigned char x;
指針類型:
typedef int* IPointer; //為整型指針int* 起一個別名,IPointer
IPointer p1; //等價于:int*p1
定義數(shù)組類型:
typedef char NAME[10]; //定義一個字符型數(shù)組類型,命名為NAME,共計10個元素。
NAME name; //等價于:char name[10];
通過typedef統(tǒng)一數(shù)組類型的使用,也能簡化代碼,是程序易于閱讀。也可以將類型定義與編譯預處理指令搭配使用。
#include <iostream>
using namespace std;
#define _UNICODE
#ifdef _UNICODE;
typedef wchar_t TCHAR;
#else
typedef char TCHAR;
#endif
int main()
{
TCAR ch;
/* 代碼段 */
return 0;
}
6.2 枚舉類型
布爾類型的一個主要特征是其值域只有2個取值,即真和假,分別用關(guān)鍵字true和false表示,實際的程序處理任務也會碰到和bool類型相似的數(shù)據(jù)類型,他們的值是有限的,我們稱他們的值域是可枚舉的。例如一個星期只有星期一到星期日,這個值域就是可枚舉的值域。
c++語言可以將值域可枚舉的數(shù)據(jù)定義成新的數(shù)據(jù)類型,這些數(shù)據(jù)類型被統(tǒng)稱為枚舉類型,值域中的每個取值稱之為一個枚舉元素。
enum 枚舉類型名 {枚舉常量1,枚舉常量2,... ...,枚舉常量n};//n是有限的
- enum是定義枚舉類型的關(guān)鍵字
- 枚舉類型名需要符合標識符命名規(guī)則,
- 枚舉常量是表示各個枚舉元素的名稱,須符合標識符的命名規(guī)則,相當于是一個符號常量
- 計算機內(nèi)部儲存枚舉型數(shù)據(jù)時,用整數(shù)表示每個枚舉常量,默認情況下,枚舉常量1=0,枚舉常量2=1,... ...,枚舉常量n=n-1,可以定義時為枚舉常量另行指定其他的值。
enum WeekDay{sun,mon,tur,thu,fri,sat};//默認取值。sun=0,mon=1,......
enum WeekDay{sun=7,mon=1,tur=2,wed=3,thu=4,fri=5,sat=6};//指定元素取值
enum WeekDay{sun=7,mon=1,tur,wed,thu,fri,sat};//等價上一條語句
演示程序:
#include <iostream>
using namespace std;
enum WeekDay{sun,mon,tur,thu,fri,sat};
int main(){
enum WeekDay x;//定義1個WeekDay類型的枚舉變量x
x=mon;//為x賦值,mon是枚舉常量,內(nèi)部數(shù)值是1
cout<<x<<endl;//顯示枚舉類型對應的數(shù)值,結(jié)果為1
return 0;
}
注:枚舉變量不能直接用整數(shù)賦值,因為兩者數(shù)據(jù)類型不同。
6.3 聯(lián)合體類型和結(jié)構(gòu)體類型
c++語言可以將多個變量組合在一起形成一個邏輯上的整體,使變量成為整體的一個成員,變量成員的類型可以相同,也可以不同,將多個變量成員組成的整體定義成新的數(shù)據(jù)類型,這種數(shù)據(jù)類型能夠描述程序涉及所面臨的復雜數(shù)據(jù)。
-
聯(lián)合體類型
如果多個變量成員不會同時使用,那就可以將它們定義成聯(lián)合體類型,聯(lián)合體類型的特點是變量成員共用同一內(nèi)存單元,同一時刻只能保存一個成員的數(shù)據(jù)。定義聯(lián)合體的目的是節(jié)約內(nèi)存。
語法:
union 聯(lián)合體類型名{ 數(shù)據(jù)類型1 變量成員名1; 數(shù)據(jù)類型2 變量成員名2; ... ... 數(shù)據(jù)類型n 變量成員名n;};- union是定義聯(lián)合體類型的關(guān)鍵字
- 聯(lián)合體類型名需符合標識符的命名規(guī)則
- 變量成員之間的數(shù)據(jù)類型可以相同,也可以不同,但變量名不能相同。
代碼實例:
/*定義聯(lián)合體變量數(shù)據(jù)類型 UType*/ union UType{ char ch; int x; double y; }; /*使用聯(lián)合體類型可以定義聯(lián)合體變量,定義時需要union關(guān)鍵字,聯(lián)合體變量定義好后, 可以訪問其中的成員,訪問聯(lián)合體成員的語法形式是:聯(lián)合體變量名.成員名其中圓點"."稱為成員運算符*/ union UType a; //定義聯(lián)合體變量a a.ch;a.x; a.y; //訪問聯(lián)合體變量成員聯(lián)合體類型也稱為共用體類型,每個聯(lián)合體變量所占用的字節(jié)數(shù),等于其最大成員的字節(jié)數(shù)。因為聯(lián)合變量的成員是共用同一內(nèi)存單元,故聯(lián)合體變量同一時刻只能保持一個成員的數(shù)據(jù)。應當準確理解聯(lián)合體類型的這個特點,否則會造成數(shù)據(jù)丟失。
-
結(jié)構(gòu)體類型
如果多個變量成員同時使用,那就將他們定義成結(jié)構(gòu)體類型,結(jié)構(gòu)體類型的特點是個變量成員單獨分配內(nèi)存單元,分別保存各自的數(shù)據(jù)。定義結(jié)構(gòu)體數(shù)據(jù)的目的是存儲復雜數(shù)據(jù)。
c++語言可以將多個變量組合在一起形成邏輯上的整體,使變量稱為整體的一個成員,變量成員的類型可以相同,也可以不同,如果多個變量成員需要同時使用,那就可以將它們定義成結(jié)構(gòu)體類型。
演示實例:
int main() { char ID[11],Name[9];//保存學號和姓名 int Age;//保存年齡 double Source;//保存成績 strcpy(ID,"1510078620"); strcpy(Name,"李若溪"); Age=19; Score=90; /*在上述代碼種,學生的學號,姓名,成績,年齡都存在關(guān)聯(lián)關(guān)系可以將其看作一個整體。 它們都是某個學生個人信息的一部分。學生信息需要同時包含這些部分,否則信息就不完整了。 所以可以將學生信息定義成結(jié)構(gòu)體類型*/ }定義結(jié)構(gòu)體類型語法:
struct 結(jié)構(gòu)體類型名{ 數(shù)量類型1 變量成員名1; 數(shù)量類型2 變量成員名2; ... ...; 數(shù)據(jù)類型n 變量成員名n;};- struct是定義結(jié)構(gòu)體類型的關(guān)鍵字;
- 結(jié)構(gòu)體類型名需符合標識符的命名規(guī)則
- 變量成員之間的數(shù)據(jù)類型可以相同,也可以不同,但變量名不能相同。
演示代碼:
/*定義結(jié)構(gòu)體類型Student*/struct Student{ char ID[11],Name[9]; int Age; double Score; }; struct Student a; //定義結(jié)構(gòu)體變量a a.ID; a.Name; a.Age; a.Score; //訪問各個成員與聯(lián)合體變量不同的是,結(jié)構(gòu)體數(shù)據(jù)類型的各個成員,單獨分配內(nèi)存單元,分別保存各自的數(shù)據(jù),每個結(jié)構(gòu)體變量占用的字節(jié)數(shù),等于其所有成員占用字節(jié)數(shù)的總和。
結(jié)構(gòu)體類型的指針變量
我們也可以定義結(jié)構(gòu)體的指針變量來保存某個結(jié)構(gòu)體的內(nèi)存地址。然后通過指針變量間接訪問結(jié)構(gòu)體變量及其成員,實例:
struct Student a; struct Student *p; //定義**結(jié)構(gòu)體類型的指針變量** p = &a; //獲取結(jié)構(gòu)體變量a的內(nèi)存地址 (*p).ID;(*p).Name;(*p).Age;(*p).Score; //通過指針間接訪問內(nèi)部成員。 p->ID; p->Name; p->Age; p->Score;//通過指向運算符,間接訪問。由于通過(*p)的方式訪問結(jié)構(gòu)體成員比較繁瑣,c++語言引入了一種新的更加直觀的運算符,就是指向運算符:”->“,