close

[CS] C++ 跨檔案公用變數

 

  好久沒寫一些有營養的東西,實在是因為不知道要寫什麼...今天來寫點基本的東西好了~

  只要有稍微寫過一點程式的朋友,應該都聽變數(variable) 這東西吧。所謂變數,就是電腦某一塊可使用的記憶體,讓我們能將數字存在裡面,隨時可以拿出來運算。

  我們也知道使用變數之前一定要宣告(declare),所謂宣告就是向作業系統要求一塊記憶體當做變數來使用,而這時我們必須告訴作業系統,這變數的資料型態(data type)是什麼?數量有多少?以及您要用什麼符號(symbol)來代表這個變數,也就是用什麼符號來代表這塊記憶體的位址。

  宣告若正確,記憶體也足夠,作業系統就會分配一塊記憶體給您使用。那麼這塊記憶體就可以一直讓我們永遠地使用嗎?

  當然不行!您也不會希望有這種事發生!若我們要求的變數都不會被回收,就會發生memory leak現象,也就是記憶體有塊地方永遠無法被再利用,除非重開機,memory leak會造成記憶體空間遲早會被耗盡的情形。位了減少這種現象,除了作業系統有提供資源回收的機制(garbage collection),它會自動判斷程式中那些變數將來是否會不再使用,進而將它回收。程式語言也嚴格的規定一個機制,那就是變數的生命週期(scope of variables)。

  這在一般程式語言的課本都有詳細介紹,我稍微描述一下就好。變數的生命週期大致上分兩種,區域變數(local variables)、全域變數(global variables)。所謂區域變數就是這個變數只會存活在一個範圍,比如一個函式(function)、副程式(subroutine),是以堆疊(stack)來配置記憶體,隨著這個範圍的結束,堆疊就會清除,變數就會被收回。全域變數就是這個變數在程式一開始就將記憶體空間配置好,因而會存活在整個程式,隨著程式結束才會被收回。

  很多教程式語言的老師都建議學生盡量不要使用全域變數,我本人實在不能認同,如此教法只會教出一堆把程式寫的極難維護的人才。全域變數可以用來作為監控整個程式的信號旗標(flag),尤其在開發視窗應用程式是十分重要的技術。若能將全域變數適當的使用,整個程式架構將會變的非常穩固清楚。

  回到主題,很多C++初學者會遇到一個問題,如果我把整個程式寫在一個cpp檔,全域變數使用上都沒問題。但如果分成數個cpp檔,如main()在main.cpp,其他副程式寫在sub.cpp,main()與副程式都會使用到某個全域變數 a ...問題就來了,a一開始為0, main() 裡設為10,結果在副程式sub1()裡讀到卻是 0而不是10,如下所示

main.cpp sub.h sub.cpp
#include "sub.h"
void main(void){
  a = 10;
  sub1();
}
#ifndef SUB_H
#define SUB_H
#include <stdio.h>

int a = 0;
void sub1();
#endif
#include "sub.h"
void sub1(){
   printf("%d",a);
}

  這種全域變數的範圍只是各程式碼檔案而已,檔案之間的變數生命是獨立的。

  如何宣告出跨檔案的全域變數有很多方法,我這邊列出常用的兩種。第一種是利用外在變數(extern)的宣告,上例可以這樣改寫

main.cpp sub.h sub.cpp
#include "sub.h"
void main(void){
  a = 10;
  sub1();
}
#ifndef SUB_H
#define SUB_H
#include <stdio.h>

extern int a;
void sub1();
#endif
#include "sub.h"
int a = 0;
void sub1(){
   printf("%d",a);
}

  我來解釋一下,sub.h裡宣告了一個外在變數叫 a,如此任何其他cpp檔只要include sub.h這個檔,就會知道有一個int變數叫 a可供使用,這個動作只是讓大家認識有 a 這號人物而已,但是a還尚未被分配到記憶體空間,這在編譯(compiling)階段會成功,但在連結(link)階段就會出錯了。所以我們必須在任一cpp檔的任一地方去實際的宣告他,如sub.cpp第二行所示,如此才算把這個全域變數宣告完成。

  這方法適用在C及C++,但若是全域變數的數量很多,就會變的不太容易管理了。第二個方法就是以物件類別(class)來產生全域變數。我們會用到靜態變數(static variables),靜態變數可以讓區域變數內的值得以永久保存,直到程式結束。我們可以利用這特性來產生全域變數。請看下例:

clsGlobal.h clsGlobal.cpp
#ifndef CLS_GLOBAL_H
#define CLS_GLOBAL_H
class CGlobal{
public:
  static int a;
};
#endif
#include "clsGlobal.h"
int CGlobal::a = 0;

 

main.cpp sub.h sub.cpp
#include "sub.h"
void main(void){
  CGlobal::a = 10;
  sub1();
}
#ifndef SUB_H
#define SUB_H
#include <stdio.h>
#include "clsGlobal.h"

void sub1();
#endif
#include "sub.h"

void sub1(){
   printf("%d",a);
}

  我們用一個物件CGlobal來管理全域變數,一樣也是要在clsGlobal.h先讓大家知道有a這個東西,然後在clsGlobal.cpp裡要求記憶體空間。之後要使用它只要在前面加上CGlobal::這個命名範圍符號就可以。這個方法只能在C++裡使用,因為C沒有物件這種觀念。這也是我最喜歡的方法,因為用個物件來分類這些全域變數,對程式將來的維護是有效率的!而且,也蠻炫的~(這是我自己發明的方法喔~如有雷同,純屬巧合...)

 

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 丘猴子 的頭像
    丘猴子

    轉貼部落格

    丘猴子 發表在 痞客邦 留言(4) 人氣()