從C語言到C++
1、面向對象的C++
c++是在C語言的基礎上發展起來的一門語言,C++是即支持結構化程序編程又支持面向對象程序設計的混合型語言。他一方面提供了對C的兼容性,保持了C的簡介、高效,接近匯編語言的特點,另一方面,C++引入了類、對象的概念,可以進行類的繼承和派生,使C++成為一種面向對象的程序設計語言。早期的具有面向對象性能的程序語言存在很多缺點,如建立對象不凡便,與用戶交互能力差等。C++克服了這些缺點,實現了真正的可視化編程。用戶使用C++時,不用自己一一建立對象,只要在C++提供的框架內添加實現某種功能的代碼即可。
2、C++對C語言的改進
c++與C語言兼容,C語言中的數據類型、運算符、表達式、函數定義和調用、預處理命令等在C++中都是適用的,還包括語句格式等。C++繼承了C的風格和特點,但同時又對C的不足和問題做出了改進,主要包括:
(1)增加了一些新的運算符,使得C++應用起來更加方便,如::,new,delete,.*,->等。
(2)改進了類型系統,增加了安全性。C語言中類型轉換很不嚴格,而C++規定類型轉換大多采用強制轉換,函數的說明必須使用原型,還對默認類型做了限制。
(3)增加了“引用”概念,是的引用函數參數更加方便。
(4)允許函數重載,允許設置默認參數,這些措施提高了編程的靈活性,還減少了冗余性。
(5)引進了內聯函數的該概念,提高了程序的效率。
(6)對變量說明更加靈活,在滿足先定義後使用的前提下,局部變量的定義和聲明可以在程序塊的任何位置。
3、C++的應用場景
C++並非萬能藥,這裏舉出一些C++的適用時機。
- C++適合構造程序中需求較穩定的部分,需求變化較大的部分可使用腳本語言;
- 程序須盡量發揮硬件的最高性能,且性能瓶頸在於CPU和內存;
- 程序須頻繁地與操作系統或硬件溝通;
- 程序必須使用C++框架/庫,如大部分遊戲引擎(如Unreal/Source)及中間件(如Havok/FMOD),雖然有些C++庫提供其他語言的綁定,但通常原生的API性能最好、最新;
- 項目中某個目標平臺只提供C++編譯器的支持。
按應用領域來說,C++適用於開發服務器軟件、桌面應用、遊戲、實時系統、高性
4、使用Visual Studio創建一個win32控制臺應用程序
用Visual Studio不能單獨編譯一個.cpp或者一個.c文件,這些文件必須依賴於某一個項目,因此必須創建一個新項目,下面創建一個控制他應用程序。
點擊確認後,即進入了如下界面
再輸入程序如下:
#include "stdafx.h" #include "iostream" using namespace std; int main() { cout << "hello world!\n"; return 0; }
#include<iostream>為預處理命令,預處理器包含在編譯器中。iostream為C++自帶的庫文件,它包含標準流cin、cout輸入和輸出,當程序中需要用到這些輸入輸出流對象時,要用#include將其包含在程序中。
第二行using namespace std;,該語句是說明程序使用C++標準庫的命名空間std。命名空間是ANSI C++引入的可以由用戶命名的作用域,用來處理程序中常見的同名沖突。這個再後面會進行講解。
運行程序,可以得到如下結果:
·運行結果如下:
5、C++對C的擴充
1、C++的輸入輸出
C++本身沒有專門的輸入輸出語句,C++的輸出和輸入不是C++本身定義的,而是編譯系統提供的I/O庫中定義的,是用“流”的方式實現的,使用的是iostream庫。
(1)輸出流cout
cout<<表達式1[<<表達式2<<..<<表達式n];
該語句功能是:依次輸出表達式1、表達式2、...表達式n的值。具體輸出內容可以是一個整數、實數、字符及字符串。雖然cout不是C++本身提供的語句,但為了方便,常常由cout和流插入運算符<<實現輸出的語句稱為輸出語句或cout語句。下面是一個實例:
#include "stdafx.h" #include "iostream" using namespace std; int main() { cout << "this is a c++ program"<<endl; }
在執行該語句是,系統先把插入的數據順序存放在輸出緩沖中,直到輸出緩沖區滿或遇到cout語句中的endl(或‘\n‘,ends,flush)為止。將緩沖去中已有的數據一起輸出,並清空緩沖區。
endl表示輸出一個換行字符,同時刷新流,如果不用endl,還可以使用轉移字符‘\n‘來表示換行。
其中使用cout語句需要註意以下幾點:
- 一個插入運算符<<只能插入一個輸出項,多個輸出項時要有多個插入運算符<<,例如,要輸出變量a、b、c,不能寫成cout<<a,b,c。而應寫成cout<<a<<b<<c。
- cout輸出時,用戶不必通知計算機以何種類型輸出,系統會自動判別輸出數據的類型,使輸出的數據按相應的類型輸出。即使定義變量a、b、c時是不同類型的,但輸出是可以直接寫如下語句:cout<<a<<‘ ‘<<b<<endl
- 一條語句包含多個輸出項時,可以分成若幹行書寫,也可以用多條語句輸出。
例如:cout<<"this is a C++ program"<<endl可以寫成:
cout << "this " << "a C++" << "program" << endl;
或者:
cout << "this "; cout << "a C++"; cout << "program"; cout << endl;
(2)、輸入流cin
cin語句一般格式
cin>>變量>>[變量>>...>>變量n];
該語句功能為:運行程序時從鍵盤輸入變量1、變量2、...變量n的值。
使用該語句需要註意以下幾點:
- 一個提取運算符>>只能插入一個輸入項,如果有多個輸入項,要用多個提取運算符>>
運行程序是,從鍵盤上輸入多個變量,多個值之間用空格、Tab或回車鍵分開。
- cin與cout類似,系統也會根據變量的類型從輸入流中提取相應長度的字節。
例如:
// example1.cpp: 定義控制臺應用程序的入口點。 // #include "stdafx.h" #include "iostream" using namespace std; int main() { char c1, c2; int a; float b; cin >> c1 >> c2 >> a >> b; }
需要註意的是不能用cin語句把空格字符和回車換行符作為字符輸入給字符變量,他們將被跳過。如果想將空格字符或者回車換行符輸入給字符變量,可以使用getchar()函數。
- 與cout類似,一個cin語句可以分寫成若幹行。
(3)、格式控制
利用格式控制符可以進行格式化的輸入和輸出。用oct、dec、hex分別將輸入或輸出的數值換成8進制、10進制及16進制。例如:
cout << oct << a << endl;//輸出a的8進制 cout << dec << a << endl;//輸出a的10進制 cout << hex << a << endl;//輸出a的16進制
此外,還有很多格式控制符,例如:
ws:輸入流的時候刪掉空白字符。
ends:輸出一個null字符。
endl:輸出一個換行字符,同時刷新流。
flush:刷新流。
下面通過一個實例加深了解
// example1.cpp: 定義控制臺應用程序的入口點。 // #include "stdafx.h" #include "iostream" using namespace std; int main() { char a[20]; cin >> a; cout << a << endl; return 0; }
這裏發現,輸入aaa bbb輸出卻只為aaa,這是因為遇見空格後就結束了,後面BB無法讀取出來,所以輸出的是aaa.
2、變量的存儲類型
1、變量的作用域和生存期
變量的作用與即變量的作用範圍。有的變量可以在整個程序或其他程序中使用,有的則只能在局部範圍內使用。按作用域範圍範圍可以將變量分為兩種:局部變量和全局變量。
變量的生存期是指變量從生成到被撤銷的這段時間。實際上就是變量占用內存的時間。按生存期長短可將變量分為兩種:動態變量和靜態變量。
變量只能在其生存期裏被引用,變量的作用域直接影響變量的生存期。作用域和生存期是從空間和時間的兩個不同的角度來描述變量的特性。
- 局部變量的作用域和生存期
在一個函數內部定義的變量就是局部變量,其作用域只在本函數範圍內有效,也就是說只有在本函數內才能使用他們,在此函數之外是不能使用這些變量的,這是因為函數內的變量是保存在棧內的,而所有棧內的內容在函數調用結束後會被清除。所以,局部變量的生存期是從函數被調用的時刻開始到函數返回調用處的時刻(靜態變量除外)結束。在使用局部變量時,需要註意以下幾點:
- 主函數main()中定義的變量也是局部變量,它只能在主函數中使用,其他函數不能使用。同時,主函數中也不能使用其他函數中定義的局部變量。
- 形參變量屬於被調用函數的局部變量;實參則屬於全局變量或調用函數的局部變量。
- 允許在不同的函數中使用相同的變量名,他們代表不同的對象,分配不同的單元,互不幹擾,也不會發生混淆。
- 在復合語句中定義的變量也是局部變量,其作用域只在符合語句範圍內。其生存周期是從復合語句被執行的時刻到復合語句執行完畢的時刻。
- 在函數聲明中出現的參數名,其作用範圍只在本行的括號內。實際上,編譯系統對函數中聲明的變量是忽略的,即使在調用函數時也沒有為它們分配存儲內存。例如:
int max(int a, int b);//函數聲明a、b int max(int x, int y)//函數定義,形參是x、y { cout << x << y << endl;//合法,x、y在函數內有效 cout << a << b << endl;//非法,a、b在函數體中無效。 }
編譯時,系統認為函數體中的a\b未經定義。
2.全局變量作用域和生存期
在函數外部做定義說明的變量,稱為外部變量。它屬於整個源程序文件。這是因為全局變量是保存在堆中的,堆內的數據可以從程序開始運行一直到程序運行結束。其作用域從定義變量的未知開始到源文件結束,或者是有extern說明的其他源文件。全局變量的生存期和程序相同。使用時需要註意以下幾點:
- 應盡量少使用全局變量,因為全局變量在程序執行過程中始終占用內存單元,降低了函數的獨立性、通用性、可靠性及可移植性,降低了程序的清晰性,容易出錯。
- 若全局變量與局部變量同名,則全局變量被屏蔽。要引用全局變量,則必須在變量名前加上兩個冒號::。
- 全局變量定義必須在所有的函數之外,且只能定義一次,並可賦初始值。全局變量定義的一般形式為:
[extern] 類型說明符全局變量名1[ =初始值1],...,全局變量名n[ =初始值n];
- 對全局變量進行聲明,可擴展全局變量的作用域。全局變量說明的一般形式為:
extern 類型說明符 全局變量名1,...,全局變量名n;
2、變量的存儲類型
在C++中變量除了有數據類型的屬性之外,還有存儲類別的屬性。存儲類別指的是數據在內存中的存儲方法。存儲方法分為靜態存儲和動態存儲兩大類。具體包含四種:自動(auto)、靜態的(static)、寄存器的(register)和外部的(extern)。
考慮了變量的存儲類型後,變量定義的完整形式應為:
存儲類型說明符 數據類型說明符 變量名1,變量名2,...變量名n; 例如: auto char c1,c2; register i; static int a,b; extern int x,y;
- 自動變量
程序中大多數變量屬於自動變量。函數中的局部變量,如果不用關鍵字static加以聲明,編譯系統對它們是動態的分配存儲空間的。函數的形參和在函數中定義的變量(包括在符合語句中定義的變量)都屬於此類。在調用該函數時,系統給形參和函數中定義的變量分配存儲空間,數據存儲在動態存儲區中,在函數調用結束後自動釋放這些空間。如果是在復合語句中定義的變量,則在變量定義時分配存儲空間,在符合語句結束時自動釋放空間。如果在符合語句中定義的變量,則在變量定義時分配存儲空間,在復合語句結束時自動釋放空間。因此者類局部變量稱為自動變量。自動變量用關鍵字auto作存儲類別的聲明。
例:
int f(int a) //定義f函數,a為形參 { auto int b, c = 3; //定義b和c為整型的自動變量 }
存儲類型auto與int的順序是任意的,而且關鍵字auto還可以省略,如果不寫auto,則系統默認為自動存儲模式,它屬於動態存儲方式。
註意,用auto、register、static聲明變量時,是在定義的基礎上加上這些關鍵字,而布恩那個單獨使用。
2.靜態變量
靜態變量在整個程序生命周期內,其地址靜止不變。
靜態變量的類型說明符是static。靜態變量屬於靜態存儲類型,但靜態存儲類型的變量不一定是靜態變量。例如,外部變量雖屬於靜態存儲類型,但不一定是靜態變量,必須用static加以定義後才能稱為靜態外部變量。
全局變量改變為靜態變量後會改變它的作用域,限制了它的使用範圍。當一個源程序由多個源文件組成時,非靜態的全局變量可通過外部變量說明使其在多個文件中都有效。而靜態全局變量只在定義該變量的源文件內有效,在同一項目的其他源文件中不能使用。
自動變量可以用static改變為靜態自動變量,改變後,其生存周期為整個源程序,但是作用域與自動變量相同。此外需要註意的是,靜態局部變量賦字操作只運行一次,此後再調用,不再進行賦值。
例:
#include "stdafx.h" #include "iostream" using namespace std; int main() { int i; void func(); //函數說明 for (i = 1; i <= 5; i++) func(); //函數調用 return 0; } void func() //函數定義 { static int j = 1; //靜態局部變量,其只運行一次。 ++j; cout << j << ""; }
可以看到,五次調用函數func,但是賦值給j=1的操作只執行了一次。
3.寄存器變量(register型變量)
一般情況下,變量的值是存放再內存中的,當程序中用到哪一個變量的值時,由控制器發出指令將內存中該變量的值送到CPU的運算器中,而寄存器變量存放在CPU的寄存器中,使用時,不需要訪問內存,而是直接從寄存器中讀寫,這樣可提高效率。
寄存器變量的說明符為register,屬於動態存儲類型。只有局部自動變量和形式參數才可以定義為寄存器變量。
4.外部變量
外部變量(即全局變量)是在函數的外部定義的,它的作用域為從變量的定義開始,到本程序文件的末尾。在此作用域內,本文件的各個函數都可以引用全局變量。編譯時將全局變量分配到靜態存儲區。
如果外部變量不在文件的開頭定義,其有效的作用範圍只限於定義處到文件結束。如果在定義之前想引用該全局變量,則應該在引用之前用關鍵字extern對該變量做外部變量聲明,表示該變量是一個在後面定義的全局變量。有了此聲明,就可以從聲明處起,合法的引用該全局變量,這種聲明稱為提前引用聲明。如果程序由多個文件組成,在一個文件中定義的外部變量,在另一個文件中對該外部變量進行聲明後,也可以合法的引用該外部變量。
用extern擴展全局變量的作用域,雖然給程序帶來了方便,但會使程序的可讀性變差,修改不便,使用時要慎重。
3、函數的默認參數
在C和C++中使用函數時,包括函數的聲明、定義、調用三部分,都要遵守相應的規則。
1、函數的聲明
函數聲明就是把函數的名字、函數類型以及形參的個數、類型和順序通知編譯系統,以便在遇到函數調用時,核查調用形式是否與聲明相符。
函數聲明的一般形式:
函數類型 函數名(參數類型1 參數名1,參數類型2 參數名2,...); 函數類型 函數名(參數類型1,參數類型2..);
第二種聲明是對第一種聲明的簡化,它省略了參數名,因為函數聲明不涉及函數體,所以編譯系統不關心參數是什麽。
2、函數的定義
函數定義是指對函數功能的實現,包括指定函數名、函數類型、形參及其類型、函數體等,他是一個完整的、獨立的函數單位。
函數定義的一般形式:
函數類型 函數名(形式參數表) { 聲明部分; 函數體主體; return 返回值; }
3、函數的調用
使用該函數,叫做函數調用,函數調用方式是:
函數名(實際參數表)
只要函數聲明出現在函數調用之前,就可以把包含函數體的函數定義移到函數調用的後面。因此,在程序中調用函數由以下兩種方式:
方式1: 函數聲明; 函數調用; 函數定義; 方式2 函數定義; 函數調用;
例如之前的例子,還可以這樣寫:
#include "stdafx.h" #include "iostream" using namespace std; void func() //函數定義 { static int j = 1; //靜態局部變量,其只運行一次。 ++j; cout << j << ""; } int main() { int i; for (i = 1; i <= 5; i++) func(); //函數調用 return 0; }
4、函數的默認參數
C++允許給函數形參賦予默認值。所謂默認值就是在調用時,可以不必給出某些參數的值,編譯器會自動把默認值傳遞給調用語句。對於函數的形參,可以給出默認值,也可以不提供默認值,還可以指對形參的一部分給出默認值。默認值在函數參數較多時是非常有用的。可以只傳必需的值。
使用默認參數時,需要註意以下幾點:
- 默認參數設置位置
參數的默認值可以在聲明中或定義中設置,但只能在其中一處設置,不允許在兩處同時設置。如果函數的定義在函數調用之後,則只能在函數聲明中設置默認參數。因為此時如果在定義中設置,編譯器不知道哪個參數設置了默認值。
- 帶默認參數的函數調用
設置了默認值的參數,函數調用是可以不再給值,直接讀取默認值,也可以不區默認值,重新賦值。
例如:
#include "stdafx.h" #include "iostream" using namespace std; int add(int a, int b = 5); //函數聲明 int main() { int a = 1; int b = 2; cout<<add(a, b) << endl; //函數調用,第二個參數沒有取默認值 cout << add(a) << endl; //函數調用,第二個參數取默認值 return 0; } int add(int x,int y) //函數定義 { int z; z = x + y; return z; }
輸出結果為:
- 默認參數的順序規定
如果一個函數中有多個默認參數,則默認參數應從右到左逐漸定義。即當某個參數是默認參數,那麽它後面的參數必須都是默認參數。例如:
int add(int a,int b,int c=1); //true int add(int a=1,int b=1,int c=1); //true int add(int a=1,int b=1,int c); //false
當調用函數時 ,傳進去的實參個數必須大於或等於無默認值的形參的個數,匹配參數時是從左至右去匹配。例如,對三個參數都是默認參數的,正確的調用格式為:
add() //三個參數都取默認值1,函數值為3 add(2,3) //a=2,b=3,c取默認值1,函數值為6 add(3,4,5)//a、b、c都不取默認值,結果為12
- 參數默認值的限定
在前面的例子中,參數默認值都是常量,實際上,默認值可以是全局變量,甚至是一個函數調用。
例如
int m = 1; //m為全局變量 int fun(int i = m); //正確,參數默認值為全局變量m int add(int x; int y = fun()); //正確,add函數的參數默認值為fun()函數值,而且,fun()函數調用使用的是參數默認值。
但默認值不能是局部變量,因為默認參數的函數調用是在編譯時確定的。
4、函數的重載
函數編程過程中,經常會遇到這種情況,就是需要編寫若幹個函數,他們的功能相似,但是參數不同,可以統一給這些函數取一個相同的名字,但設置不同的參數,編譯系統在函數調用時能夠將各個函數區分開來。如果兩個函數名字相同並且在相同的域中被聲明,但是參數表不同,那麽他們就是重載函數。
重載函數必須是參數類型或參數個數不同。使用重載函數需要註意:
- 重載函數都應在一個域中被聲明,不同域中聲明的同名函數不是重載函數。
- 只有函數的返回類型不同,參數相同的不是重載函數。因為函數調用時系統無法根據函數返回類型確定執行哪個函數,因此會在編譯時認定為是重復定義。
- 不要把功能不同的函數放在一起重載。這樣會破壞程序的可讀性和可理解性。
- 如果有函數重載,同時有些函數的形參帶默認值時,這樣有可能引發歧義,編譯系統無法確定調用哪個函數,因而產生錯誤。
函數重載要求編譯器能夠唯一的確定調用一個函數應執行哪個函數代碼,即采用哪個函數實現。進行函數重載時,要求同名函數在參數個數上不同,或者參數類型上不同,否則,將無法進行重載。
例:編寫兩個重載求和函數,一個計算兩個整數的和,一個計算兩個浮點型數的和。
#include "stdafx.h" #include "iostream" using namespace std; int add(int x, int y); //函數聲明 double add(double a, double b); int main() { cout<<add(2, 3) << endl; //函數調用,第二個參數沒有取默認值 cout << add(2.2,3.3) << endl; //函數調用,第二個參數取默認值 return 0; } int add(int x,int y) //函數定義 { return x + y; } double add(double a, double b) //函數定義 { return a + b; }
5、內聯函數
內聯函數是C++引進的新概念,在C語言中沒有。內聯函數具有一般函數的特性,他與一般函數的不同之處只在於函數調用的處理。一般函數進行調用時,要將程序執行權轉到被調用函數中,執行完被調用函數後才再次返回到調用它的函數中;而內聯函數是在編譯時直接將內聯函數的函數體代碼嵌入到調用函數中,所以內聯函數被執行時,不涉及到流程的轉出和返回,也不涉及到參數傳遞,提高了執行效率。
內聯函數是在函數聲明或函數定義是,在函數名前加一個inline。示例如下
inline int add(int x,int y) //函數定義 { return x + y; }
使用內聯函數有以下註意事項:
- 如果一個函數被指定為inline函數,則它將在函數中每個調用點上被內聯的展開,使用內聯函數是一種以空間換時間的行為,所以函數內語句較長或包含復雜的控制語句,如循環語句、if語句或switch語句或遞歸時,不宜使用內聯函數。
- 關鍵字inline必須與函數定義放在一起才能使函數稱為內聯,僅僅放在函數聲明前起不到任何作用。
- 並不是所有加了inline的函數都是內聯函數,inline對於編譯器來說只是一個建議,編譯器可以選擇忽略該建議,自動進行優化。所以當inline中出現遞歸、循環或者過多代碼時,編譯器自動將其作為普通函數調用。
- 在C++類中,類體內的成員函數自動被當成內聯函數,應用非常廣。
- 當編寫復雜的應用程序時,內聯函數的定義要放在頭文件中,如果其他文件要調用這些內聯函數的時候,只要包含這個頭文件就可以了。
6、引用和引用傳遞
1、引用的定義
引用是C++中提供的一個新概念,它與指針密切相關。引用是一個變量的別名,定義引用類型變量,實質上是給一個已定義的變量起一個別名,系統不會為引用類型變量分配內存空間,只是使引用類型變量和其相關聯的變量使用同一個內存空間。
定義引用類型變量的一般格式為:
<數據類型> &<引用名>=<變量名> //&不是取地址符,是引用的標識 或 <數據類型> &<引用名> (變量名)
例如:
int a=3; int &ra = a;
這裏,ra就是一個引用,他是變量a的別名。引用ra和變量a不僅值相同,地址也相同。對引用進行的計算,例如:ra=ra+2;實質上是a加上2,a的結果為5.
使用引用的註意事項如下:
- 引用必須初始化,因為它只是某個變量的別名,只能在定義引用的同時給它賦值。
- 除了初始化,引用不能再賦新值,即引用再其整個生命周期中是不能被改變的,只能依附於同一個變量。
- 不能建立數組的引用,因為數組名表示的是一組數據的起始地址,它自己不是一個真正的數據類型。
- 不能對引用再進行引用。
下面通過一個實例加深了解。
#include "stdafx.h" #include "iostream" using namespace std; int main() { int a; int &ra = a;//將ra引用為a cout << "a=" << a << endl; cout << "ra=" << ra << endl; cout << "address of a is :" << &a << endl; cout << "address of ra is :" << &ra << endl; int b = 8; ra = b; cout << "a=" << a << endl; cout << "b=" << b << endl; cout << "ra=" << ra << endl; cout << "address of a is :" << &a << endl; cout << "address of b is :" << &b << endl; cout << "address of ra is :" << &ra << endl; return 0; }
運行結果:
2、引用傳遞
引用傳遞是指將引用作為函數參數來實現的函數參數的傳遞。
一般的,函數形參為一般變量,調用時實參與形參之間參數傳遞只能是從實參到形參,是單向的。從被調用函數的角度來說,參數的值只能傳入,不能傳出,也就是通常的值傳遞。當用指針作為函數參數,調用時將實參的地址初始化成形參的指針,則可以實現實參和形參的雙向傳遞,即地址傳遞。
引用傳遞是指引用作為函數的形參,當調用函數是,對應的形參就是相應實參的別名。在調用函數內對形參的修改就是對實參的修改;在調用函數外對實參的修改,當進入被調用函數內時,相應的形參就是已經修改的實參,實現了參數的雙向傳遞。
例:利用自定義函數交換兩個變量的值,要求用引用作為函數形參。
下面程序中自定義函數的形參為引用。
#include "stdafx.h" #include "iostream" using namespace std; void swap(int &a, int &b) { int temp; temp = a; a = b; b = temp; cout << a << " " << b << endl; } int main() { int x = 1; int y = 2; cout << x << " " << y << endl; swap(x, y); cout << x << " " << y << endl; return 0; }
這裏,形參實參間采用的是引用傳遞,被調函數的形參誰然也是局部變量,但是它存放的是由主調函數放進來的實參變量的地址。被調函數對形參的任何操作都被處理成間接的尋址,即通過棧中存放的地址訪問主調函數中的實參變量。正因為如此,被調函數對形參做的任何操作都會影響主調函數中的實參變量。
3、引用作為函數返回值
函數定義時,函數名前加&號,可以將引用作為函數返回值,當使用引用作為返回值時需要註意以下幾點:
- 不能返回局部變量或臨時變量的引用,但可以返回全局變量的引用,也就是說要註意被引用的對象不能超出作用域。
- 不能返回函數內部動態分配的內存的引用。因為被函數返回的引用只是作為一個臨時變量出現,而沒有被賦予一個實際的變量,那麽這個引用所指向的由new分配的空間就無法被釋放,從而造成內存泄漏。
- 可以返回類成員的引用,但最後是cost常量。這是因為當對象的屬性是與某種業務規範相關聯的時候,其賦值常常與某些其他屬性或者對象的狀態有關,於是有必要將賦值操作封裝在一個業務規則中。如果其他對象可以獲得該屬性的非常量引用,那麽對該屬性的單純賦值會破壞業務規則的完整性。
#include "stdafx.h" #include "iostream" using namespace std; double arr[5] = { 1,2,3,4,5 }; double &change(int i) { return arr[i]; } int main() { int i; cout << "origin data is:"; for (i = 0; i < 5; i++) cout << arr[i] << " "; cout << endl; change(2) = 3.14; change(3) = -97; cout << "Modified data is:"; for (i = 0; i < 5; i++) cout << arr[i] << " "; cout << endl; return 0; }
7、用const定義常變量
用const類型修飾符說明的類型稱為常類型,常類型的變量或對象的值是不能被更新的。使用const來定義的常量,具有不可以變性,可以避免意義模糊的數字出現,方便進行參數的調整和修改,提高執行效率,便於進行類型檢查,是編譯器對處理內容有更多了解,消除隱患。
1、用const修飾一般變量
通過const關鍵字將一個變量定義為常量。例如:const int a=10;,如果在程序中試圖修改a的值,則會引起一個錯誤。由於const類型的量一經定義就不能改變它的值,因此在定義時必須初始化。用const定義常變量格式為:
const 數據類型 變量名 = 表達式; const int a=10; 或 數據類型 const 變量名 = 表達式;
int const a=10;
2、const修飾指針變量
用const修飾指針變量,不同的寫法會有不同情況。下面舉例說明:
(1)const int *pi和int const *pi的意義相同,表示const修飾的類型為int的指針變量pi為常量,因此,pi的內容為常量且不可變。即指針所指向的內容是常量不可變,語句等價為const(int) *pi和(int) const *pi;
(2) char *const pi等價於const (char*) pi,表示const修飾的類型為char*的變量pi為常量,因此,pi指針本身為常量不可變。
(3)const char* const pi 表示指針本身和指針內容兩者皆為不可變的。
3、const限定函數參數
const可以用來限定函數參數,例如:
void Fun(const int Var);
被限定的參數在函數體中不可變,由值傳遞的特點可知,即使Var在函數體中被改變了,也不會影響到函數外部。所以此限定與函數的使用者無關,僅與函數的編寫者有關。
4、const限定函數的值型返回值
const可以用來限定函數返回值,例如:
const int Fun2()
它表示被限定函數的返回值不可被更新,當函數返回值是內部的類型時,已經是一個數值,不可被賦值更新。此時無意義,最好去掉。但是這個例子說明了,當函數返回值是自定義的類型時,這個類型仍然包含可以被賦值的變量成員,用const可以限定返回值不能更改。
5、const限定類、對象、對象引用
const修飾類、對象時表示該對象為常對象,其中的任何成員都不能別修改。對於對象指針和對象引用也是一樣。const修飾的對象,該對象的任何非const成員函數都不能被調用,因為任何非const成員函數都會有可能修改成員變量。
8、字符串變量
在C語言中,沒有字符串型的數據類型,需要字符串類型變量時是通過字符數組或字符指針來實現的。在C++中除了上述方法外,還可以通過包含頭文件string,然後用string來實現字符串變量的定義。string是在C++標準庫中聲明的一個字符串類,用該類可以定義對象,每一個字符串變量都是string類的一個對象實例。使用時,可以將string看作一個新的數據類型——字符串類型,用它定義的變量就稱為字符串變量。
1、字符串變量的定義
和其他類型變量一樣,字符串變量也必須先定義後使用,定義字符串變量要用類名string。格式為:
string 變量1[,變量2,...變量n]
如果對變量初始化,則格式為:
string 變量名1=字符串表達式1[,變量名2=字符串表達式2,....變量名n=字符串表達式n
示例
string string1; string string2="chian";
2、字符串變量的賦值
在定義了字符串變量後,可以進行賦值,格式為:
字符串變量=字符串表達式;
根據字符串表達式的不同,分為以下三種情況:
- 字符表達式可以說字符串常量,例如:string1=“canada";
- 字符表達式是另一個字符串變量。例如:string2=string1;只要string1和string2均為字符型變量即可。
- 字符串表達式可以是由運算符、常量、變量組成的表達。例如:string2=”hello“+string1;
3、字符串變量的輸入輸出
可以在輸入輸出語句中用字符串變量名,直接進行字符串的輸入輸出。例如:
cin >> string; //從鍵盤輸入要給字符串給字符串變量string1 cout << string2; //輸出string2
另外,定義一個字符串變量後,也可以將其當作一個字符數組使用,字符串變量名即為字符數組名;例如
string word="hello" cout<<word[0];
3、字符串變量的運算
用字符串數組表示字符串變量時,其運算必須用專門的字符串函數。而用string定義的字符串變量可以直接使用簡單的運算符進行運算。例如,用賦值好進行字符串復制,用加號進行連接,用關系運算符進行字符串比較等。
#include "stdafx.h" #include "iostream" #include <string> using namespace std; int main() { string name; cout << "please enter your name!" << endl; cin >> name; cout << endl << name + "welcome to C++ wrold!" << endl; return 0; }
9、內存動態分配與撤銷運算符new和delete
用new和delete可以動態開辟、撤銷地址空間。
1、new及其用法
在程序運行時,用new運算符動態分配內存後,將返回一個指向新對象的指針,即所分配的內存地址空間的起始地址。用戶可以通過這個地址,並用這個地址來訪問對象。如果分配失敗則返回0,通常將地址賦值給一個指針變量。一般格式為:
類型名 *指針變量= new 類型名; 類型名 *指針變量= new 類型名(初值);//對存入該地址的數據初始化
要註意的是類型名即可是int、char等基本數據類型,也可以是結構體、類等用戶自定義的類型;用new既可以給單個變量動態分配內存,也可以給數組動態分配內存。
- 單變量的動態內存分配
例如:
int *pa; pa= new int ; *pa=8;
該語句的作用是,首先定義一個整型指針變量pa,用new動態分配用於存放指針變量pa,將首地址存入指針變量pa,並將數值8存入該地址。
以上三條語句可以合並為以下一條
int *pa= new int(8);
- 數組內存動態分配
一般格式為:
類型名 *指針變量= new類型名[數組大小] 例如: int *ps; ps=new int[10];
該語句的作用是:首先定義要給整型變量ps,用new動態分配用於存放一個包含10個元素的整型數組的內存空間,將首地址存入變量ps。
2、delete及其用法
動態內存的生存周期由程序員決定,如果不及時釋放內存,程序將在最後才釋放掉動態內存。一般的,如果某動態內存不再使用,需要將其釋放掉,否則,會發生內存泄漏現象。delete和new要成對使用。用delete釋放內存空間的語句格式為:
delete 指針名; //用於釋放單變量 delete []指針名; //用於釋放數組 例如 delete pa; delete []ps;
10、命名空間
在單人完成程序時,只要小心一點,是可以不發生重名的。但是一個大項目往往不是一個人完成的。由於相同的語言規則,那麽不同人完成過程中,類名、全局函數名、全局變量名、重復是很常見的事,尤其是許多程序員合作並可能調用第三方庫的時候,如果沒有命名空間或者類似機制的話,項目在實體命名討論時就要花費大量時間,為了解決命名沖突,C++提出了命名空間的概念。它實際上是一個由程序設計者命名的內存區域,程序設計者可以根據需要制定一些有名字的空間域,把一些全局實體分別放在各個命名空間中,從而與其他實體分隔開來。就好比兩個函數或類中定義相同名字的對象一樣,利用作用域標識符”::”加以區分。
對於命名空間的使用,C++標準程序庫中所有標識符都被定義在一個名為std的命名空間中。使用C++標準庫的某個標識符時,可以有如下三種方法:
- 使用作用域運算符直接指定標識符。例如:
std::cout<<std::hex<<3.4<<std::endl;
- 使用using聲明來表示:
using std::cout; using std::endl; cout<<"hello world!"<<endl
- 使using namespace std編譯命令,例如:
#include <iostream> using namespace std;
采用這種格式,命名空間std內定義的所有標識符都可以直接使用,就好像他們被聲明為全局變量一樣。
使用命名空間std的方法很多,但不能貪圖方便,重視用第三種,這樣就失去了命名空間設計的意義。一般情況下,按照以下原則選擇使用方式:
- 對偶爾用到的命名空間成員,應該使用第一種方法
- 對於較大命名空間中的經常使用的少數幾個成員,提倡使用第二種方法。
- 對於需要反復使用同一個命名空間的多個成員,使用using編譯命名,即第三種方法。
在C++語言中,命名空間使用namespace來聲明,並使用{}來界定命名空間的作用域。例如
namespace foo {int num=0}
C++強大背後
從C語言到C++