1. 程式人生 > 其它 >學習C++要注意的那點事

學習C++要注意的那點事

C與C++標頭檔案的區別和聯絡

1、舊的 C++ 標頭檔案,如 iostream.h、fstream.h 等將會繼續被支援,儘管它們不在官方標準中,這些標頭檔案的內容不在名稱空間 std 中。新的 C++ 標頭檔案,如 iostream、fstream 等包含的基本功能和對應的舊版標頭檔案相似,但標頭檔案的內容在名稱空間 std 中。

2、標準C標頭檔案如 stdio.h、stdlib.h 等繼續被支援,標頭檔案的內容不在 std 中。具有C庫功能的新C++標頭檔案具有如 cstdio、cstdlib 這樣的名字,它們提供的內容和相應的舊的C標頭檔案相同,只是內容在 std 中。

名稱空間宣告使用的注意事項

#include <iostream>
using namespace std;  //宣告名稱空間std
int main(){
    cout<<"......."<<endl;
return 0;
}

  將std直接宣告在所有函式外部,這樣雖然使用方便,但在中大型專案開發中是不被推薦的(小型專案或者測試學習宣告在全域性變數中還是很方便的),這樣做增加了命名衝突的風險,推薦在函式內部宣告std

#include <iostream>
void func(){
    using namespace std;  //必須重新宣告
    cout<<"http://c.biancheng.net"<<endl;
}
int main(){
    using namespace std;  //宣告名稱空間std
    cout<<"。。。。。。"<<endl;
func();
return 0;
}

Newdelete

  和 malloc() 一樣,new 也是在堆區分配記憶體,必須手動釋放,否則只能等到程式執行結束由作業系統回收。為了避免記憶體洩露,通常 new deletenew[] delete[] 操作符應該成對出現,並且不要和C語言中 malloc()free() 一起混用。

例如:

int *p = new int;  //分配1個int型的記憶體空間
delete p;  //釋放記憶體
----------------------------------------或----------------------------------------
int *p = new int[10];  //分配10個int型的記憶體空間
delete[] p;

函式的預設引數

#include<iostream>
using namespace std;
//帶預設引數的函式
void func(int n, float b=1.2, char c='@'){
    cout<<n<<", "<<b<<", "<<c<<endl;
}
int main(){
    //為所有引數傳值
    func(10, 3.5, '#');
    //為n、b傳值,相當於呼叫func(20, 9.8, '@')
    func(20, 9.8);
    //只為n傳值,相當於呼叫func(30, 1.2, '@')
    func(30);
    return 0;
}

  C++規定,預設引數只能放在形參列表的最後,而且一旦為某個形參指定了預設值,那麼它後面的所有形參都必須有預設值。實參和形參的傳值是從左到右依次匹配的,預設引數的連續性是保證正確傳參的前提。

函式過載

函式的過載的規則:

  1. 函式名稱必須相同。
  2. 引數列表必須不同(個數不同、型別不同、引數排列順序不同等)。
  3. 函式的返回型別可以相同也可以不相同。
  4. 僅僅返回型別不同不足以成為函式的過載。
//交換 int 變數的值
void Swap(int *a, int *b){
    int temp = *a;
    *a = *b;
    *b = temp;
}
//交換 float 變數的值
void Swap(float *a, float *b){
    float temp = *a;
    *a = *b;
    *b = temp;
}
//交換 char 變數的值
void Swap(char *a, char *b){
    char temp = *a;
    *a = *b;
    *b = temp;
}
//交換 bool 變數的值
void Swap(bool *a, bool *b){
    char temp = *a;
    *a = *b;
    *b = temp;
}

C++是如何做到函式過載的

  C++程式碼在編譯時會根據引數列表對函式進行重新命名,例如void Swap(int a, int b)會被重新命名為_Swap_int_intvoid Swap(float x, float y)會被重新命名為_Swap_float_float。當發生函式呼叫時,編譯器會根據傳入的實參去逐個匹配,以選擇對應的函式,如果匹配失敗,編譯器就會報錯,這叫做過載決議(Overload Resolution)。不同的編譯器有不同的重新命名方式,這裡僅僅舉例說明,實際情況可能並非如此。從這個角度講,函式過載僅僅是語法層面的,本質上它們還是不同的函式,佔用不同的記憶體,入口地址也不一樣。

函式模板

值(Value)和型別(Type)是資料的兩個主要特徵,它們在C++中都可以被引數化。

  所謂函式模板,實際上是建立一個通用函式,它所用到的資料的型別(包括返回值型別、形參型別、區域性變數型別)可以不具體指定,而是用一個虛擬的型別來代替(實際上是用一個識別符號來佔位),等發生函式呼叫時再根據傳入的實參來逆推出真正的型別。這個通用函式就稱為函式模板(Function Template)。

#include <iostream>
using namespace std;
template<typename T> void Swap(T *a, T *b){
    T temp = *a;
    *a = *b;
    *b = temp;
}
int main(){
    //交換 int 變數的值
    int n1 = 100, n2 = 200;
    Swap(&n1, &n2);
    cout<<n1<<", "<<n2<<endl;
    //交換 float 變數的值
    float f1 = 12.5, f2 = 56.93;
    Swap(&f1, &f2);
    cout<<f1<<", "<<f2<<endl;
    return 0;
}

  template是定義函式模板的關鍵字,它後面緊跟尖括號<>,尖括號包圍的是型別引數(也可以說是虛擬的型別,或者說是型別佔位符)。typename是另外一個關鍵字,用來宣告具體的型別引數,這裡的型別引數就是T。從整體上看,template<typename T>被稱為模板頭。

定義模板函式的語法:

template <typename 型別引數1 , typename 型別引數2 , ...>返回值型別 函式名(形參列表){
  //在函式體中可以使用型別引數
}

typename關鍵字也可以使用class關鍵字替代,它們沒有任何區別。型別引數不能為空,多個型別引數用逗號隔開。

類模板

宣告類模板的語法為:

template<typename 型別引數1 , typename 型別引數2 , …> class 類名{
  //TODO:
};

模板頭和類頭是一個整體,可以換行,但是中間不能有分號。例如:

template<typename 型別引數1 , typename 型別引數2 , …>  //這裡不能有分號
class 類名{
  //TODO:
};

例子:

template<typename T1, typename T2>  //這裡不能有分號
class Point{
public:
    Point(T1 x, T2 y): m_x(x), m_y(y){ }
public:
    T1 getX() const;  //獲取x座標
    void setX(T1 x);  //設定x座標
    T2 getY() const;  //獲取y座標
    void setY(T2 y);  //設定y座標
private:
    T1 m_x;  //x座標
    T2 m_y;  //y座標
};

上面的程式碼僅僅是類的宣告,我們還需要在類外定義成員函式。在類外定義成員函式時仍然需要帶上模板頭,格式為:

template<typename 型別引數1 , typename 型別引數2 , …>
返回值型別 類名<型別引數1 , 型別引數2, ...>::函式名(形參列表){
  //TODO:
}

第一行是模板頭,第二行是函式頭,它們可以合併到一行,不過為了讓程式碼格式更加清晰,一般是將它們分成兩行。

template<typename T1, typename T2>  //模板頭
T1 Point<T1, T2>::getX() const /*函式頭*/ {
    return m_x;
}
template<typename T1, typename T2>
void Point<T1, T2>::setX(T1 x){
    m_x = x;
}

  除了 template 關鍵字後面要指明型別引數,類名 Point 後面也要帶上型別引數,只是不加 typename 關鍵字了。另外需要注意的是,在類外定義成員函式時,template 後面的型別引數要和類宣告時的一致。

強制型別轉換

強制型別轉換的格式為:

(type_name) expression

type_name為新型別名稱,expression為表示式。例如:

(float) a;  //將變數 a 轉換為 float 型別
(int)(x+y);  //把表示式 x+y 的結果轉換為 int 整型
(float) 100;  //將數值 100(預設為int型別)轉換為 float 型別

  可以自動進行的型別轉換一般風險較低,不會對程式帶來嚴重的後果,例如,int double 沒有什麼缺點,float int 頂多是數值失真。只能強制進行的型別轉換一般風險較高,或者行為匪夷所思,例如,char * int * 就是很奇怪的一種轉換,這會導致取得的值也很奇怪,再如,int char * 就是風險極高的一種轉換,一般會導致程式崩潰。
使用強制型別轉換時,程式設計師自己要意識到潛在的風險。