C++ 入門指南 V2.00 - 單元 26 - 記憶體管理




啟動程式 (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

重點整理


  1. 程式執行後會由作業系統載入記憶體中,作業系統依程式需求分配記憶體空間給程式。
  2. 小程式除非用耗費記憶體的演算法,不然比較不會有記憶體空間不足的問題,大程式若沒有做好記憶體管理就出現記憶體問題。
  3. 解構函數是物件結束時所執行的函數,通常用來刪除資料成員。
  4. 建構函數中的初值串列用來初始化資料成員。

問題與討論


  1. 為什麼要自己做記憶體管理?難道不能由編譯器直接編譯為做好記憶體管理的程式嗎?
  2. 什麼是解構函數?解構函數跟建構函數有什麼不同?
  3. 為什麼指標成員要用 -> 來存取?不能用小數點就好嗎?

練習


  1. 承接上一個單元的猜數字遊戲,將新程式寫在 exercise2601.hexercise2601.cpp 中,繼續實作 Run() 與所需的成員函數。
  2. 承上題,另寫一個程式 exercise2602.cpp 建立 GuessGame 物件,然後呼叫執行 Run()

the end

沒有留言: