1. 程式人生 > 其它 >C++語言對C語言的擴充

C++語言對C語言的擴充

1.1 C++的特點

C++語言既保留了C語言的有效性、靈活性、便於移植等全部精華和特點,又添加了面向物件程式設計泛型程式設計的支援,具有強大的程式設計功能,可方便地構造出模擬現實問題的實體和操作;編寫出的程式具有結構清晰、易於擴充等優良特性,適合於各種應用軟體、系統軟體的程式設計。用C++編寫的程式可讀性好,生成的程式碼質量高,執行效率僅比組合語言慢10%~20%。

1.2 C++語言的副檔名

為了使編譯器能夠區別是C語言還是C++語言,C++語言體系規定用“.cpp”(意即C Plus-Plus)作為C++語言原始檔的副檔名以區別於C語言用的“.C”副檔名。雖然僅差兩個字母,但編譯時的處理卻相差甚遠。“cpp

”的副檔名與作業系統無關。與C++語言原始檔相關的標頭檔案副檔名一般仍用“.h”,但有些作業系統也有規定使用“.hpp”充當標頭檔案副檔名的。

1.3 註釋符

  • [x] 段註釋: /* */
  • [x] 單行註釋:// 當只做單行註釋時便可用“//”符號表示從此符號起至行尾均為行註釋內容。
  • [x] 程式編譯時將忽略所有的註釋內容。

1.4 名稱空間

名稱空間域是隨標準C++而引入的。它相當於一個更加靈活的檔案域(全域性域),可以用花括號把檔案的一部分括起來,並以關鍵字namespace開頭給它起一個名字:

namespace ns1
{
     float a, b, c;
     fun1(){    }
}

花括號括起來的部分稱宣告塊。宣告塊中可以包括:類、變數(帶有初始化)、函式(帶有定義)等。在域外使用域內的成員時,需加上名稱空間名作為字首,後面加上域操作符“::” 。這裡添加了名稱空間名稱的成員名被稱為限定修飾名。如:ns1::ans1::fun1()等等。
最外層的名稱空間域稱為全域性名稱空間域(Global Namespace Scope),即檔案域。
名稱空間域可分層巢狀,同樣有分層遮蔽作用。例如:

namespace n1 {
    namespace n2 {	    // 名稱空間巢狀
        class matrix{   }	// 名稱空間類成員matrix
    }
}

訪問matrix類,可寫成:n1::n2::matrix

使用using宣告可只寫一次限定修飾名。using宣告以關鍵字using開頭,後面是被限定修飾的(qualified)名字空間成員名,例如:
using n1::n2::matrix; // 名稱空間類成員matrix的using宣告
以後在程式中使用matrix時,就可以直接使用成員名,而不必使用限定修飾名。
使用using指示符可以一次性地使名字空間中所有成員都可以直接被使用,比using宣告方便。using指示符以關鍵字using開頭,後面是關鍵字namespace,然後是名字空間名。

標準C++庫中的所有元件都是在一個被稱為std的名字空間中宣告和定義的。在採用標準C++的平臺上使用標準C++庫中的元件,只要寫一個using指示符:using namespace std;
就可以直接使用標準C++庫中的所有成員。
注意:如果使用了名空間std,則在使用#include編譯預處理命令包含標頭檔案時,必須去掉標頭檔案的副檔名.h,否則會出錯。

1.5 C++語言的輸入輸出

C++語言另外定義了一套保留字與運算子來替代C語言中對標準輸入、輸出函式的引用。C++語言的保留字為:

cout << "輸出內容" << …;       // cout為標準輸出流物件(預設輸出到顯示器)
cin >> "輸入內容" >> …;        // cin為標準輸入流物件(預設從鍵盤輸入)
#include <iostream.h>

舉個栗子

#include<iostream>            // 使用名空間std,則必須去掉.h副檔名
using namespace std; 
void main() {  
    char  name[10];
    int  age;
    cout << "請輸入您的名字:";
    cin >> name;
    cout << "請輸入您的年齡:";
    cin >> age;
    cout << "姓名為:" << name << endl;
    cout << "年齡為:" << age << endl;
}

1.6 變數的定義

在C語言中,區域性變數說明必須置於可執行程式碼段之前,不允許區域性變數宣告和可執行程式碼混合在一起。但C++在變數的定義上作了兩種較大的改變

  • [x] 一是允許變數的定義語句可以出現在程式的任何位置,使得區域性變數的定義點與使用點不至於離得太遠,增強程式的可讀性,而且也不必在編寫某一程式塊的開始時就考慮要用到哪些變數;
  • [x] 二是允許直接使用結構體名定義變數,這種擴充套件為程式設計師在程式設計中提供了不少方便。類似地在C++語言中聯合名、列舉名也可在定義後獨立地作為型別名使用。

1.7 強制型別轉換

  • [x] 通過強制型別轉換,得到一個所需型別的中間值,該中間值被引用後即自動釋放。原來表示式的值型別並未改變。如下列程式碼段:
    int b; float f; f = float(b); // 此時變數b仍然為int型別。

  • [x] 強制型別轉換符優先順序較高,只對緊隨其後的表示式起作用,而對其他部分不起作用。如表示式float(i) * f的含義是先將變數i強制型別轉換為float型別,然後與變數f運算。

  • [x] 強制型別轉換應當用在不做轉換將影響表示式結果的正確性或精度,或不能完成相應運算的場合。而對於系統可以自動轉換型別的場合,則沒有必要使用。

1.8 動態記憶體的分配與釋放

1.new運算子

指標變數 = new 資料型別;

  • [x] new從堆記憶體中為程式分配可以儲存某種型別資料的一塊記憶體空間,並返回指向該記憶體的首地址,該地址存放於指標變數中。

  • [x] 堆記憶體可以按照要求進行分配,程式對記憶體的需求量隨時會發生變化,有時程式在執行中可能會不再需要由new分配的記憶體空間,而且程式還未執行結束,這時就需要把先前佔用的記憶體空間釋放給堆記憶體,以後重新分配,供程式的其它部分使用。

2.delete運算子

運算子delete用於釋放new分配的記憶體空間,它的使用形式為:delete 指標變數;
其中的指標變數儲存著new動態分配的記憶體的首地址。
舉個栗子

#include <iostream>
using namespace std;
void main(){
    int *p;
    p = new int;    // 分配記憶體空間
    *p = 5;
    cout << *p;
    delete p;       // 釋放記憶體空間
}

在用new分配記憶體的同時進行初始化。使用形式為:指標變數 = new 資料型別(初始值);
例如上例中的:p = new int; *p = 5;
也可寫成:p=new int(5);

3. 注意:

(1) 用new獲取的記憶體空間,必須用delete進行釋放;
(2) 對一個指標只能呼叫一次delete;
(3) 用delete運算子作用的物件必須是用new分配的記憶體空間的首地址。

4.用new建立陣列型別的變數

指標變數 = new 資料型別[陣列大小];
此時指標變數指向第一個陣列元素的地址。使用new分配陣列時,不能提供初始值。使用new建立的陣列變數也由delete釋放。其形式為:delete 指標變數;delete [ ]指標變數;
同樣,也可以用new來為多維陣列分配空間,但是除第一維可以為變數外,其它維數都必須是常量。

注意在使用delete時,不用考慮陣列的維數。
有時,並不能保證一定可以從堆記憶體中獲得所需空間,當不能成功地分配到所需要的記憶體時,new返回0,即空指標。因此我們可以通過判斷new的返回值是否為0,來得知系統中是否有足夠的空閒記憶體來供程式使用。例如:

int *p = new int[100];
if (p == 0) {
    cout<< "已終止, 不能分配更多記憶體. "<<endl;
    exit(1);
}

其中exit函式的作用是終止程式執行。

舉個栗子:從堆記憶體中獲取一個整型陣列,賦值後並打印出來。

#include <iostream>
using namespace std;
int main() { 
    int n; // 定義陣列元素的個數
    int *p;            
    cout << "請輸入陣列長度 : ";
    cin >> n;
	
    if ((p = new int[n]) == 0) {    // 分配記憶體空間
        cout<< "已終止, 不能分配更多記憶體. "<<endl;
        exit(1); 
    }         
    for (int i = 0; i < n; i++) {	   
        p[i] = i * 2;
    }
    cout << "輸出陣列: " << endl;
    for (i = 0; i < n; i++) {
        cout << p[i] << "  ";
    }
    cout << endl;
    delete []p;     // 釋放記憶體空間
    return 0;
}

1.9 作用域運算子::

通常情況下,如果全域性變數與區域性變數同名,那麼區域性變數在其作用域內具有較高的優先權。C語言規定只能在變數的作用域內使用該變數,不能使用其他作用域中的變數,可採用C++中提供的作用域運算子::,它能指定所需要的作用域。
注意:不能用::訪問函式中的區域性變數。在C++語言中作用域運算子::還用來限定類的成員

#include <iostream>   
using namespace std;
float attack = 2000;        // 全域性變數
int main(){  
    int attack = 1000;      // 區域性變數
    cout << attack << endl;
    cout << ::attack << endl;    // ::a表示全域性作用域中的變數a
    return 0;
} 

1.10 引用

引用是C++語言的一個特殊的資料型別描述,用於在程式的不同部分使用兩個以上的變數名指向同一地址,使得對其中任一個變數的操作實際上都是對同一地址單元進行的。在這種兩個以上變數名的關係上,被宣告為引用型別的變數名則是實際變數名的別名。
引用運算子為&,宣告引用的一般形式為:
資料型別 &引用變數名 = 變數名; 資料型別 &引用變數名 = 變數名;資料型別 &引用變數名 = 變數名;
對引用進行操作,實際上就是對被引用的變數進行操作。引用不是值,不佔儲存空間,宣告引用時,目標的儲存狀態不會改變。引用一旦被初始化,就不能再重新賦值。
舉個栗子

#include <iostream>
using namespace std;
int main(){  
    int num = 50;	
    int &ref = num;
	ref += 10;
	cout << "num = " << num << endl;
	cout << "ref = " << ref << endl;
        num += 40;
	cout << "num = " << num << endl;
	cout << "ref = " << ref << endl;
	return 0;
}

說明:
(1) 在一行上宣告多個引用型變數(函式)名時,要在每個變數(函式)名前都冠以“&”符號。
(2) 引用不是變數,所以引用本身不能被修改,在程式中對引用的存取都是對它所引用的變數的存取。
(3) 一個變數被宣告為引用時必須進行初始化,除非這個引用是用作函式的引數或返回值,為引用提供的初始值應為變數(包括物件)。引用一旦被初始化,就不能再重新賦值。如ref = &j;ref = j;是允許的。
(4) 由於引用不是變數,所以,不能說明引用的引用,也不能說明陣列元素的型別為引用陣列,或指向引用的指標。例如:
int &a[5]; // 錯誤
int &*p; // 錯誤
由於指標也是變數,因此可以說明對指標變數的引用。例如:

int *a;  
int *&p = a;   
int b;  
p = &b;    // a指向變數b

(5) 引用與指標不同。指標的內容或值是某一變數的記憶體單元地址,而引用則與初始化它的變數具有相同的記憶體單元地址。指標是個變數,可以把它再賦值成其它的地址,然而,建立引用時必須進行初始化並且決不會再指向其它不同的變數。
(6) 要注意區分引用運算子和地址運算子的區別。例如:

int num = 50;
int &ref = num;
int *p = &ref;

(7) 可以用一個引用初始化另一個引用。例如:

int num = 50;
int &ref1 = num;
int &ref2 = ref1;
ref2 = 100;			// num被修改為100

其中ref2也是對num的引用。
(8) 可以把函式的引數說明成引用以建立函式引數的引用傳遞方式
(9) 有空指標,無空引用
(10) 引用不能用資料型別來初始化。如:int&ref=int; // 錯誤
(11) 函式呼叫可以作為左值
引用表示式是一個左值表示式,因此它可以出現在形、實引數的任何一方。若一個函式返回了引用,那麼該函式的呼叫也可以被賦值。一般說,當返回值不是本函式內定義的區域性變數時就可以返回一個引用。在通常情況下,引用返回值只用在需對函式的呼叫重新賦值的場合,也就是對函式的返回值重新賦值的時候。避免將區域性作用域中變數的地址返回,就使函式呼叫表示式作為左值來使用。

栗子:統計學生中A類學生與B類學生各為多少個。A類學生的標準是平均分在80分以上,其餘都是B類學生。

#include <iostream>   
using namespace std;
int array[6][4] = {{60, 80, 90, 75}, {75, 85, 65, 77}, {80, 88, 90, 98},
                 {89, 100, 78, 81}, {62, 68, 69, 75}, {85, 85, 77, 91}};
int& level(int grade[], int size, int& tA, int& tB);
int main() {
    int typeA = 0,typeB = 0;
    int student = 6;
    int gradesize = 4;
    for (int i = 0; i < student; i++)   // 對所有的學生資料進行處理
        level(array[i], gradesize, typeA, typeB)++; // 函式呼叫作為左值
    cout << "number of type A is " << typeA << endl;
    cout << "number of type B is " << typeB << endl;
    return 0;
}
int& level(int grade[], int size, int& tA, int& tB) {
    int sum = 0;
    for(int i = 0; i < size; i++)   // 成績總分
        sum += grade[i];
    sum /= size;                  // 平均分
    if(sum >= 80) return tA;    // A類學生
    else    return tB;          // B類學生
}
程式的執行結果為:
number of type A is 3
number of type B is 3

栗子:返回的區域性作用域內的變數,函式作為左值。

#include <iostream>   
using namespace std;
float& fn2(float r){
    float temp;
    temp = r * r * 3.14;
    return temp;          // 返回了局部變數
}
int main() {
    fn2(5.0) = 12.4;
    /* 返回的是區域性作用域內的變數,函式呼叫作為左值使用。此種情況應儘量避免。*/
    return 0;
}

此程式在編譯時會出現如下的警告資訊:
warning C4172: returning address of local variable or temporary

1.11 const修飾符

1.12 字串

1.13 C++語言中函式的新特性