啟動程式 (program) 是由作業系統 (operating system) 載入記憶體 (memory) 中,因此程式會被作業系統分配記憶體空間,空間大小依程式需求而定
記憶體的問題在大部分的小程式中比較看不出來,而在小程式結束執行後,通常作業系統會自動回收利用原先小程式所佔去的記憶體空間,因為說真的現在電腦的記憶體都很足夠,除非小程式是用很耗記憶體的演算法 (algorithm) ,或是同時間執行太多小程式,不然很難感覺出有什麼記憶體不足的問題。
然而大型程式就不一樣了,打開後可能就一直執行,像是播放音樂的軟體或就像我們用的 Qt Creator ,開啟一個程式檔案後就等同新建一個物件 (object) ,然後交給另一個物件去顯示,當開啟的檔案愈多,所建立的物件也愈多,用不到的檔案視窗當然可以關閉,可是我們並沒有打算關閉 Qt Creator 啊!
假設這樣的軟體沒有做記憶體管理,也就是沒有告訴作業系統哪些物件不需要記憶體空間了,原本請求的記憶體空間也就沒有歸還給作業系統,那隨著開啟、關閉的視窗愈多,所佔據的記憶體空間也愈多,慢慢的整台電腦的記憶體就會被這種軟體佔據,拖慢電腦運作的速度。
解構函數 (destructor) 就是在物件結束時所執行的函數 (function) ,簡單說,解構函數用來銷毀屬於物件的資料成員 (data member) 。我們將 encryptwindow.cpp 用本書的語法高亮度整理如下
001 | #include "encryptwindow.h" |
002 | #include "ui_encryptwindow.h" |
003 | |
004 | EncryptWindow::EncryptWindow(QWidget *parent) : |
005 | QMainWindow(parent), |
006 | ui(new Ui::EncryptWindow) |
007 | { |
008 | ui->setupUi(this); |
009 | } |
010 | |
011 | EncryptWindow::~EncryptWindow() |
012 | { |
013 | delete ui; |
014 | } |
實作解構函數的部份
011 | EncryptWindow::~EncryptWindow() |
012 | { |
013 | delete ui; |
014 | } |
Qt Creator 預設的解構函數就是用關鍵字 (keyword) delete 刪除資料成員 ui ,這裡的刪除好比用橡皮擦擦掉鉛筆寫過的痕跡,意思是告訴作業系統原本屬於 ui 的記憶體空間不需要了,作業系統可以移作其他用途。
我們的程式只有一個視窗,結束程式等於結束視窗,雖然程式結束就會把原先配置的記憶體空間還給作業系統,有沒有自己 delete 其實程式都不容易出錯,這裡,我們保留 Qt Creator 預設提供的程式碼,後續再加入我們需要的程式碼。
下面繼續看到實作建構函數 (constructor) 的部份
004 | EncryptWindow::EncryptWindow(QWidget *parent) : |
005 | QMainWindow(parent), |
006 | ui(new Ui::EncryptWindow) |
007 | { |
008 | ui->setupUi(this); |
009 | } |
前三行其實可以合併為一行來看
004 | EncryptWindow::EncryptWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::EncryptWindow) |
這裡 EncryptWindow() 後面接了一個冒號,冒號後面是用逗號區隔的兩個設定初值的運算式 (expression) ,這是初值串列 (initializer list) ,也就是在宣告的部份直接設定初值。
運算式 QMainWindow(parent) 實際上是呼叫父類別 (superclass) 的建構函數,因為這樣才真正開通子類別 (subclass) 與父類別之間的關係,父類別的成員才得以初始化。
逗號後的 ui(new Ui::EncryptWindow) 則是初始化 ui 資料成員。
假設不寫初值串列,那就要在建構函數的主體大括弧中進行這些設定,寫成初值串列的優點不外是寫法上的簡便,同時可編譯成更有效率的執行檔。
雖說我們的小程式可能無法感覺到效率的差異,倒是就直接沿用預設的程式碼囉!
建構函數中只有一行, ui 呼叫函數成員 setupUi()
008 | ui->setupUi(this); |
這裡,因為 ui 是指標 (pointer) ,所以使用成員不是用小數點而要用 -> 運算子, setupUi() 就是用來初始化所有視窗元件的成員函數。
來實際看看預設程式跑出來的結果囉!把游標移到 Qt Creator 的左下方,點擊像是播放鍵的〔執行〕
建置完成,就會跑出一個空白的視窗
是的,預設程式就是個空白視窗,但是我們沒有看到視窗元件的程式碼,因為視窗元件程式碼是由 Qt Creator 依據 encryptwindow.ui 產生的,下面我們繼續討論設計 UI 吧!
中英文術語對照
程式 | program |
作業系統 | operating system |
記憶體 | memory |
演算法 | algorithm |
物件 | object |
解構函數 | destructor |
函數 | function |
資料成員 | data member |
關鍵字 | keyword |
建構函數 | constructor |
運算式 | expression |
初值串列 | initializer list |
父類別 | superclass |
子類別 | subclass |
指標 | pointer |
重點整理
- 程式執行後會由作業系統載入記憶體中,作業系統依程式需求分配記憶體空間給程式。
- 小程式除非用耗費記憶體的演算法,不然比較不會有記憶體空間不足的問題,大程式若沒有做好記憶體管理就出現記憶體問題。
- 解構函數是物件結束時所執行的函數,通常用來刪除資料成員。
- 建構函數中的初值串列用來初始化資料成員。
問題與討論
- 為什麼要自己做記憶體管理?難道不能由編譯器直接編譯為做好記憶體管理的程式嗎?
- 什麼是解構函數?解構函數跟建構函數有什麼不同?
- 為什麼指標成員要用 -> 來存取?不能用小數點就好嗎?
練習
- 承接上一個單元的猜數字遊戲,將新程式寫在 exercise2601.h 及 exercise2601.cpp 中,繼續實作 Run() 與所需的成員函數。
- 承上題,另寫一個程式 exercise2602.cpp 建立 GuessGame 物件,然後呼叫執行 Run() 。
the end
沒有留言:
張貼留言