CPP-類的位置(Placement of class)

當寫簡單程序時候,所有東西可以放在一個文件中,但程序較大時,這樣做並不明智。前面我們討論過如何將程序分割,即應該成對的構建程序文件。對每個函數定義文件(definition),應該有相應的函數聲明頭文件(declaration). 對類,我們有類似的原則。即應該成對的創建文件,一個頭文件和一個定義文件類的定義被放在頭文件中,相應的定義文件放置成員函數的定義,只有內聯函數的實現纔可以放在頭文件中,其它成員函數的實現應該放在另外一個單獨的cpp文件之中。如果我們有一組類,他們彼此相似,那我們可以將幾個類的定義放在同一個頭文件中。此時,定義文件應該包含所有類的成員函數的定義。

class MyClass // class declaration

暗示名字MyClass定義了一個類,他的完整定義將稍後出現。

看一個例子:類Flight, 描述一般航班。頭文件名字flight.h,包含了類Flight的定義。文件看起來如下:

//The file flight.h
#ifndef FLIGHT_H
#define FLIGHT_H
#include "clock.h"
#include <string>
using namespace std;
class Flight{
public:
void init(string flight_no,
          int dep_h,int dep_m,int arr_h,int arr_m);
void info();
void delay(int min);
private:
string no;
Clock dep,arr;
};
#endif

c++中,一個類不能定義多餘一次。上術代碼第2,3和最後一行用來防止flight.h在編譯過程中被包含多餘一次。FLIGHT_H是一個宏的名字,是一個我們自己定義的常數。第二行檢查是否這個名字的宏被定義過(#ifndef means if not defined)。 如果沒有定義,編譯器將讀取整個頭文件,直到#endif。因此,此流程確保頭文件不會被定義多於一次。因此,對所有包含類的定義的頭文件都應採用此步驟。建議宏和頭文件有相同的名字,但全部採用大寫。
此類有三個數據成員:字符數組no,包含航班號,兩個clocks,名字是dep和arr,暗示離開和達到時間。我們假設類Clock是在一個頭文件爲clock.h中定義,因此必須在程序中包含此頭文件(第4行)。

Flight有三個成員函數,init用來初始化航班,航班號,離開和到達時間,info輸出關於航班的信息。delay被調用當航班延誤。延遲時間作爲輸入參數。函數delay將更新此航班離開和達到時間。

就函數而言,頭文件不應該包含函數體的定義,僅僅是函數的聲明。此原則對類同樣適用。因此我們將成員函數的定義放在相應的定義文件“flight.cpp”中。

//the file flight.cpp
#include "flight.h"
#include <iostream>
#include<iomanip>
#include<string>

using namespace std;

void Flight::init(string flight_no,
                int dep_h,int dep_m,int arr_h,int arr_m)
{
    no = flight_no;
    dep.set(dep_h, dep_m, 0);
    arr.set(arr_h, arr_m, 0);
}

void Flight::info()
{
    cout << "Flight no " << no;
    cout << ", Dep "; dep.write(false);
    cout << ", Arr ", arr.write(false);
    cout << endl;
}

void Flight::delay(int min)
{
    for (int i = 1; i < min * 60; i++)
        dep.tick();
    for (int j = 1; j <= min * 60; j++)
        arr.tick();
}

爲了使程序知道類的定義,flight.h必須要被包括,clock.h則不需要,因爲他已經被包括在flight.h之中了。 數據成員dep和arr是類Clock的對象,Clock的成員函數用來處理這些對象。數據成員dep和arr不能直接訪問,因爲他們依賴類Clock的私有部分。
使用類Flight的程序必須包含flight.h。舉例如下:

//the file main.cpp
#include "flight.h"

void main()
{
    Flight f;
    f.init("SK1853", 8, 10, 10, 55);
    f.delay(15);
    f.info();
    system("pause");
}

頭文件不應該包含成員函數的單獨定義,一個重要例外是inline函數,它必須在頭文件中定義!!!
如clock.h可以看起來如下:


class Clock {
public:
    void set(int hour, int min, int sec);
    int read_hour() { return h; }
    int read_min() { return m; }
    int read_sec() { return s; }
    void write(bool write_sec = true);
    void tick();
private:
    int h, m, s;
};

inline void Clock::set(int hour,int min,int sec)
{
    h=hour;m=min;s=sec;
}

此處我們讓函數read_hour,read_min,read_sec和set作爲inline函數。 注意,如果函數set沒有被定義爲inline函數,它必須放在一個單獨的文件clock.cpp之中!!!

綜上,clock.h可以寫成下面的樣子:


class Clock {
public:
    void set(int hour, int min, int sec);
    int read_hour() { return h; }
    int read_min() { return m; }
    int read_sec() { return s; }
    void write(bool write_sec = true);
    void tick();
private:
    int h, m, s;
};

由於我們沒有將set, write,tick函數定義爲inline函數,因此我們需要一個單獨的cpp文件來實現這些函數,clock.cpp文件如下:

#include "clock.h"
#include<iomanip>
#include <iostream>
using namespace std;
void Clock::set(int hour, int min, int sec)
{
    h = hour; m = min; s = sec;
}
void Clock::write(bool write_sec)
{
    std::cout << setw(2) << setfill('0') << h
        << ':' << setw(2) << setfill('0') << m;
    if (write_sec)
        std::cout << ':' << setw(2) << setfill('0') << s;
}
void Clock::tick()
{
    s = (s + 1) % 60;
    if (s == 0)
    {
        m = (m + 1) % 60;
        if (m == 0)
            h = (h + 1) % 24;
    }
}

此cpp文件中包含了clock.h中定義的另外三個非inline函數的實現。將flight.h,clock.h,flight.cpp,clock.cpp和main.cpp一起編譯即可。

發佈了16 篇原創文章 · 獲贊 0 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章