一.C++程序的多文件結(jié)構(gòu)
之前雞啄米給大家看了很多比較完整的C++程序的例子,大家可能發(fā)現(xiàn)了,它們的結(jié)構(gòu)基本上可以分為三個部分:類的聲明、類的成員函數(shù)的實現(xiàn)和主函數(shù)。因為代碼比較少,所以可以把它們寫在一個文件中,但是我們實際進行軟件開發(fā)時,程序會比較復雜,代碼量比較大,
一個程序按結(jié)構(gòu)至少可以劃分為三個文件:類的聲明文件(*.h文件)、類的實現(xiàn)文件(*.cpp文件)和主函數(shù)文件(使用到類的文件),如果程序更復雜,我們會為每個類單獨建一個聲明文件和一個實現(xiàn)文件。這樣我們要修改某個類時就直接找到它的文件修改即可,不需要其他的文件改動。
雞啄米在第十九講中講生存期時有個時鐘類的例子,現(xiàn)在雞啄米給大家看下將那個程序按照上面說的結(jié)構(gòu)分到三個文件里:
// 文件1:Clock類的聲明,可以起名為Clock.h
#include <iostream>
using namespace std;
class Clock //時鐘類聲明
{
public: //外部接口
Clock();
void SetTime(int NewH, int NewM, int NewS); //三個形參均具有函數(shù)原型作用域
void ShowTime();
~Clock(){}
private: //私有數(shù)據(jù)成員
int Hour,Minute,Second;
};
// 文件2:Clock類的實現(xiàn),可以起名為Clock.cpp
#include "Clock.h"
//時鐘類成員函數(shù)實現(xiàn)
Clock::Clock() //構(gòu)造函數(shù)
{
Hour=0;
Minute=0;
Second=0;
}
void Clock::SetTime(int NewH,int NewM,int NewS)
{
Hour=NewH;
Minute=NewM;
Second=NewS;
}
void Clock::ShowTime()
{
cout<<Hour<<":"<<Minute<<":"<<Second<<endl;
}
// 文件3:主函數(shù),可以起名為main.cpp
#include "Clock.h"
//聲明全局對象g_Clock,具有文件作用域,靜態(tài)生存期
Clock g_Clock;
int main() //主函數(shù)
{
cout<<"文件作用域的時鐘類對象:"<<endl;
//引用具有文件作用域的對象:
g_Clock.ShowTime();
g_Clock.SetTime(10,20,30);
Clock myClock(g_Clock); //聲明具有塊作用域的對象myClock,并通過默認拷貝構(gòu)造函數(shù)用g_Clock初始化myClock
cout<<"塊作用域的時鐘類對象:"<<endl;
myClock.ShowTime(); //引用具有塊作用域的對象
}
在vs2010中如何生成這三個文件呢?我們可以點菜單中Project->Add Class,在彈出的對話框中選擇c++ class,然后由彈出個對話框,在class name處填上類名點finish就可以了,這樣.h文件和.cpp文件會自動生成,我們也可以點Project->Add New Item,在彈出的對話框中選擇Header File(.h)或C++ File(.cpp)來生成.h文件或.cpp文件。
Clock.cpp和main.cpp都使用#include "Clock.h"把類Clock的頭文件Clock.h包含進來。#include指令的作用就是將#include后面的文件嵌入到當前源文件該點處,被嵌入的文件可以是.h文件也可以是.cpp文件。如果不包含Clock.h,Clock.cpp和main.cpp就不知道Clock類的聲明形式,就無法使用此類,所以所有使用此類的文件都應(yīng)該包含聲明它的頭文件。關(guān)于#include指令下面雞啄米會講。
上面的程序在編譯時,由Clock.cpp和Clock.h編譯生成Clock.obj,由main.cpp和Clock.h編譯生成main.obj,然后就是鏈接過程,Clock.obj和main.obj鏈接生成main.exe可執(zhí)行文件。如果我們只修改了類的實現(xiàn)文件,那么只需重新編譯Clock.cpp并鏈接就可以,別的文件不用管,這樣就提高了效率。在Windows系統(tǒng)中的C++程序用工程來管理多文件結(jié)構(gòu),而Unix系統(tǒng)一般用make工具管理,如果大家從事Unix系統(tǒng)軟件開發(fā),就需要自己寫make文件。
二.編譯預處理程序
編譯器在編譯源程序以前,要由預處理程序?qū)υ闯绦蛭募M行預處理。預處理程序提供了一些編譯預處理指令和預處理操作符。預處理指令都要由“#”開頭,每個預處理指令必須單獨占一行,而且不能用分號結(jié)束,可以出現(xiàn)在程序文件中的任何位置。
1.#include指令
#include指令也叫文件包含指令,用來將另一個源文件的內(nèi)容嵌入到當前源文件該點處。其實我們一般就用此指令來包含頭文件。#include指令有兩種寫法:
#include <文件名>
使用這種寫法時,會在C++安裝目錄的include子目錄下尋找<>中標明的文件,通常叫做按標準方式搜索。
#include "文件名"
使用這種寫法時,會先在當前目錄也就是當前工程的目錄中尋找""中標明的文件,若沒有找到,則按標準方式搜索。
2.#define和#undef指令
如果你學過C語言,就會知道用#define可以定義符號常量,比如,#define PI 3.14 這條指令定義了一個符號常量PI,它的值是3.14。C++也可以這樣定義符號常量,但一般更常用的是在聲明時用const關(guān)鍵字修飾。C語言還用#define定義參數(shù)宏,來實現(xiàn)簡單的函數(shù)運算,比如,#define add(x,y) (x+y) 這條指令說明如果我們用到add(1,2)則預處理后就會用(1+2)代替,C++中一般用內(nèi)聯(lián)函數(shù)來實現(xiàn)。
#undef用來刪除由#define定義的宏,使其不再起作用。
3.條件編譯指令
用條件編譯指令可以實現(xiàn)某些代碼在滿足一定條件時才會參與編譯,這樣我們可以利用條件編譯指令將同一個程序在不同的編譯條件下生成不同的目標代碼。例如,我們可以在調(diào)試程序時加入一些調(diào)試語句,用條件編譯指令控制只有在debug模式下這些調(diào)試語句才參與編譯,而在release模式下不參與編譯。
條件編譯指令有5中形式:
a.第一種形式:
#if 常量表達式
程序正文 //當“ 常量表達式”非零時本程序段參與編譯
#endif
b.第二種形式:
#if 常量表達式
程序正文1 //當“ 常量表達式”非零時本程序段參與編譯
#else
程序正文2 //當“ 常量表達式”為零時本程序段參與編譯
#endif
c.第三種形式:
#if 常量表達式1
程序正文1 //當“ 常量表達式1”非零時本程序段參與編譯
elif 常量表達式2
程序正文2 //當“常量表達式1”為零、“ 常量表達式2”非零時本程序段參與編譯
...
elif 常量表達式n
程序正文n //當“常量表達式1”、...、“常量表達式n-1”均為零、“ 常量表達式n”非零時本程序段參與編譯
#else
程序正文n+1 //其他情況下本程序段參與編譯
#endif
d.第四種形式:
#ifdef 標識符
程序段1
#else
程序段2
#endif
如果“標識符”經(jīng)#defined定義過,且未經(jīng)undef刪除,則編譯程序段1,否則編譯程序段2。
e.第五種形式:
#ifndef 標識符
程序段1
#else
程序段2
#endif
如果“標識符”未被定義過,則編譯程序段1,否則編譯程序段2。
4.define操作符
define是預處理操作符,不是指令,所以不能用#開頭。使用形式為:define(標識符)。如果括號里的標識符用#define定義過,并且沒有用#undef刪除,則define(標識符)為非0,否則為0??梢赃@樣使用:
#if !define(HEAD_H)
#define HEAD_H
我們在包含頭文件時,有時多次重復包含同一個頭文件,比如下面這種情況:
// main.cpp文件
#include "file1.h"
#include "file2.h"
int main()
{
…
}
// file1.h文件
#include "head.h"
…
// file2.h文件
#include "head.h"
…
// head.h文件
...
class A
{
...
}
...
main.cpp包含了file1.h文件,file1.h又包含了head.h文件,main.cpp還包含了file2.h文件,file2.h也包含了head.h文件,那么main.cpp就包含了兩次head.h文件,在編譯時就會報錯,說head.h中的類A重復定義了。這時我們可以在被重復包含的文件head.h中使用條件編譯指令,用一個唯一的標識符來標識head.h文件是否已經(jīng)編譯過,如果已經(jīng)編譯過則不會重復編譯了。雞啄米給大家改寫下上面的head.h文件:
// head.h文件
#ifndef HEAD_H
#define HEAD_H
...
class A
{
...
}
...
#endif
在這個改好的head.h文件中,上來會先判斷HEAD_H是否被定義過,如果沒有被定義過,則head.h文件還沒參與過編譯,就編譯此文件中的源代碼,同時定義HEAD_H,標記head.h文件已經(jīng)參與過編譯。如果HEAD_H已經(jīng)被定義過,則說明此文件已經(jīng)參與過編譯,編譯器會跳過本文件左右內(nèi)容編譯其他部分,類A也不會有重復定義的錯誤了。
本文來源于雞啄米博客 http://www.jizhuomi.com/ , 原文地址:http://www.jizhuomi.com/software/70.html
本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請
點擊舉報。