01 C++的特點
C++是C語言的繼承,它既可以進行C語言的過程化程序設計,又可以進行以抽象數據類型爲特點的基於對象的程序設計(泛型編程),還可以進行以繼承和多態爲特點的面向對象的程序設計(面向對象編程)。
常用於系統開發,引擎開發等應用領域,是最受廣大程序員受用的最強大編程語言之一,支持類、封裝、繼承、多態等特性。
拓展:
面對對象編程:面向對象是一種對現實世界理解和抽象的方法、思想,通過將需求要素轉化爲對象進行問題處理的一種思想。
泛型編程:泛型編程是一種編程風格,其算法以儘可能抽象的方式編寫,而不依賴於執行這些算法的數據形式。
類:類定義了事物的屬性和它可以做到的(它的行爲)。一個類的方法和屬性被稱爲“成員”。一個類所包含的方法和數據描述一組對象的共同屬性和行爲。類是在對象之上的抽象,對象則是類的具體化,是類的實例。
封裝性:封裝使數據和加工該數據的方法(函數)封裝爲一個整體,把對象的設計者和對象的使用者分開,使用者不必知曉行爲實現的細節,可以增加安全性。
繼承性:繼承性是子類共享父類之間數據和方法的機制。一個類直接繼承其它類的全部描述,同時可修改和擴充。可以增強代碼的複用性。
多態性:對象根據所接收的消息而做出動作。同一消息爲不同的對象接受時可產生完全不同的行動,這種現象稱爲多態性。使具有不同內部結構的對象共享相同的外部接口。可以增加擴展性。
02 C的異常處理機制
在C語言中,傳統的錯誤處理方式有如下幾種:
1.直接終止程序(自殺)
例如:
int main(){
int a = 10;
int b = 20;
int c = a/0;
return 0;
}
當用gcc編譯完後,執行,會打印“浮點數例外”,然後程序結束。
這種情況是不允許的,無條件終止程序的庫無法運用到不能當機的程序裏。
2.返回一個錯誤的值,附加錯誤碼
這種錯誤處理方式也很常見,比如我們用C語言打開一個文件失敗時:
#include<stdio.h>
#include<errno.h>
int main(){
FILE* fp = fopen("test.txt","r");
if(NULL == fp){
printf("文件打開失敗,錯誤碼:%d\n",errno);
}
return 0;
}
這種情況,比較常用,但是有時不合適,例如返回錯誤碼是int,每個調用都要檢查錯誤值,極不方便,也容易讓程序規模加倍。
3.返回一個合法的值,讓程序處於某種非法的狀態
這種例子,最常見的就是 atoi 函數,例如:
#include<stdio.h>
#include<stdlib.h>
int main(){
int a = atoi("123456789"); //atoi()——把字符串轉換成整型數
int b = atoi("dasdasdcs");
printf("a = %d\n",a);
printf("b = %d\n",b);
return 0;
}
雖然b爲0,但是有一個全局變量表示了這個0屬於非法值,不是由字符串0轉過來的。
這種情況,很容易誤導調用者,萬一調用者沒有去檢查全局變量errno或者通過其他方式檢查錯誤,那是一個災難,而且這種方式在併發的情況下不能很好工作。
4.調用一個預先準備好在出現"錯誤"的情況下使用的函數
這種異常處理情況比較少見
#include<stdio.h>
#include<stdlib.h>
void DealError(){
printf("除數爲0,老兄你在逗我?\n");
}
typedef void(*fun)();
int Div(int a,int b,fun callback){
if(b==0){
callback();
return 0;
}
return a/b;
}
int main(){
printf("正常情況下的4/2 = %d\n",Div(4,2,DealError));
printf("調用錯誤處理函數的4/0 = %d\n",Div(4,0,DealError));
return 0;
}
- 通過暴力的方式解決
暴力的方式有兩種,abort()函數和常見exit()函數.例如依然處理除0問題,代碼可以這樣寫:
#include<stdio.h>
#include<stdlib.h>
int Div(int a,int b){
if(b==0){
//exit(1); //直接退出
abort(); //在Windows下會彈出一個信息框
}
return a/b;
}
int main(){
printf("正常情況下的4/2 = %d\n",Div(4,2));
printf("調用錯誤處理函數的4/0 = %d\n",Div(4,0));
return 0;
}
- 使用goto語句
雖然,goto語句十分強大,但違背了程序的順序執行,打亂的程序的執行流,盲目的使用goto語句可能會出現意想不到的錯誤,因此,並不推薦使用goto語句.
#include<stdio.h>
#include<stdlib.h>
int main(){
int a = 0;
int b = 0;
printf("請輸入兩個值:\n");
printf("a = ");
scanf("%d",&a);
printf("b = ");
scanf("%d",&b);
if(b==0){
goto Error;
}
printf("a/b = %d\n",a/b);
return 0;
Error:
printf("除數不能爲0,程序異常退出!\n");
exit(-1);
}
03 異常的拋出與捕獲
在C++中,異常的拋出和處理主要使用了以下三個關鍵字:try、 throw 、 catch.
當我們在程序中想拋出一個異常時,可以這樣:
#include<iostream>
#include<exception>
using namespace std;
int Div(int left,int right){
if(right==0){
throw exception("除數不能爲0");
}
return left/right;
}
當我們想使用這個函數時,需要在函數外部進行異常的捕獲:
int main(){
try{
Div(10,20); //合法
Div(10,30); //合法
Div(10,0); //非法,會拋出異常
}catch(exception & e){
cout<<e.what();; //打印異常信息
}
return 0;
}
如果存在不同類型的異常,我們可以這樣:
try{
//包含可能拋出異常的語句;
}catch(類型名 [形參名]){
//可能出現的異常1
}catch(類型名 [形參名]){
//可能出現的異常2
}catch(...){
//如果不確定異常類型,在這裏可以捕獲所有類型異常!
}
1.異常是通過拋出對象而引發的,該對象的類型決定了應該激活哪部分代碼.
就上述代碼來說,我們throw了一個exception對象,因此在捕獲異常時,最終會匹配到catch到exception的代碼塊.
2.被選中的處理代碼是調用鏈中與該對象類型匹配且離拋出位置最近的那個.
當try內的代碼塊出現異常時,系統會根據catch的順序和參數的匹配程度來選擇執行哪個代碼塊,因此,系統會選擇最靠前且參數越匹配的代碼塊.
3.拋出異常後會釋放局部存儲對象,所以被拋出的對象也就還給系統了,throw表達式會初始化一個拋出特殊的異常對象副本(匿名對象),異常對象由編譯管理,異常對象在傳給對應的catch處理之後撤銷。
也就是說,在上述的除法代碼中,我們throw出的對象在拋出異常後會還給操作系統,而throw表達式會自己初始化一個匿名的對象副本,在傳給catch相應的代碼塊後被回收.
04 內存溢出與內存泄漏
內存溢出 out of memory:是指程序在申請內存時,沒有足夠的內存空間供其使用,出現out of memory;比如申請了一個integer,但給它存了long才能存下的數,那就是內存溢出。
內存泄露 memory leak:是指程序在申請內存後,無法釋放已申請的內存空間,失去了對該段內存的控制,因而造成了內存的浪費。 一次內存泄露危害可以忽略,但內存泄露堆積後果很嚴重,無論多少內存,遲早會被佔光。