以 MVC 模式來看我們發展的專案 (project) , M 是指 Encrypt 類別 (class) ,也就是我們已經開發好的 encrypt.cpp , V 為 Qt Creator 自動產生的 encryptwindow.ui , C 則是 encryptwindow.cpp
View → encryptwindow.ui
Controller → encryptwindow.cpp
單元 29 才會開始整合 Encrypt 類別,我們在這個單元內先來看看 Qt Creator 自動產生的幾個檔案,下圖為新增完成的 encrypt_gui 專案資料夾
副檔名 encrpt_gui.pro 與 encrpt_gui.pro.user 是專案檔案, encryptwindow.ui 則是 GUI 介面檔案,其他還有三個程式檔案, encryptwindow.h 為 encryptwindow.cpp 的標頭檔 (header file) ,這是 EncryptWindow 類別的實作檔案,作為控制 GUI 元件之用,而 main.cpp 則是包含 main() 函數的執行程式,我們先來看看 main() 的內容囉
#include "encryptwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
EncryptWindow w;
w.show();
return a.exec();
}
我們採淺灰色表示 Qt 程式庫中識別字的語法高亮度。
首先,引進必要的標頭檔,然後在 main() 中分別建立 QApplication 及 EncryptWindow 物件,並以後者呼叫 show() 來顯示視窗,最後回傳前者呼叫 exec() ,告訴作業系統 EncryptWindow 的視窗正在執行,直到使用者結束為止。
main.cpp 在後續都不會進行修改,這是 Qt 程式的基本執行程式,所有的視窗功能都由 EncryptWindow 類別設置,因此往後只需要修改擴充 EncryptWindow 即可。
至於 EncryptWindow 類別的宣告放在 encryptwindow.h ,以下為檔案內容
#ifndef ENCRYPTWINDOW_H
#define ENCRYPTWINDOW_H
#include <QMainWindow>
namespace Ui {
class EncryptWindow;
}
class EncryptWindow : public QMainWindow
{
Q_OBJECT
public:
explicit EncryptWindow(QWidget *parent = nullptr);
~EncryptWindow();
private:
Ui::EncryptWindow *ui;
};
#endif // ENCRYPTWINDOW_H
第 1 行及第 2 行為前置處理器指令 (preprocessor directive) , #ifndef 用來判斷是否沒有定義 encryptwindow.h ,如果沒有定義則用 #define 定義
#ifndef ENCRYPTWINDOW_H
#define ENCRYPTWINDOW_H
這個前置處理的目的是為了避免重複引入相同的標頭檔,因為重複引入會產生編譯錯誤,簡單講就是編譯器 (compiler) 不知道要用哪一個標頭檔,然後用了 #ifndef 最後一行就要用 #endif 結束
#endif // ENCRYPTWINDOW_H
接下來用 namespace 宣告了命名空間 (name space) Ui
namespace Ui {
class EncryptWindow;
}
Ui 實際上定義在 ui_encryptwindow.h 裡,這是 Qt Creator 在編譯後自動建立的程式檔案, Ui 裡定義的 EncryptWindow 類別就是實際的視窗物件。
現在不需要太擔心 Ui::EncryptWindow 的內容,稍候我們會用 Designer 直接在 Qt Creator 的視窗中拉出 GUI 外觀,這部份 Qt Creator 會自動修改 ui_encryptwindow.h ,所以認識一下 Ui 是代表什麼就好囉!
EncryptWindow 繼承 (inherit) 自 QMainWindow ,至於繼承是寫在類別宣告的第 10 行,在 EncryptWindow 之後用冒號接父類別識別字
class EncryptWindow : public QMainWindow
繼承是物件導向程式設計 (object-oriented programming) 的一個特性,子類別 (subclass) 可從父類別 (superclass) 承接所有 public 或 protected 的成員 (member) ,當然也可以再定義專屬於子類別的成員。這是說如果有很多個類別具有共通的特性,就可以把共通的部份抽出來設計父類別,然後用子類別去繼承,這些子類別再各自定義自己需要的成員。
「繼承」的英文原文為 inherit ,這個詞泛指從什麼得到什麼,「遺傳」的英文原文也是 inherit ,由於中文的「繼承」與「遺傳」兩者詞義有些不同,「繼承」有隱喻前者已死,後者取代前者的意思,物件導向的機制並無此意,反而比較接近「遺傳」,不過程式界沿襲「繼承」一詞已久,特此說明。
倒是要注意一個地方,現在 encryptwindow.h 中的 EncryptWindow 與 Ui::EncryptWindow 是不一樣的類別,前者是 MVC 中的 C ,後者則是 MVC 中的 V ,為了讓 C 能控制 V ,因此 EncryptWindow 需要 Ui::EncryptWindow 型態的資料成員
private:
Ui::EncryptWindow *ui;
};
是的,像是拉出個按鈕,然後按下按鈕要有什麼反應都透過 ui 來設定就可以了,除此之外, EncryptWindow 的第一行有
Q_OBJECT
Q_OBJECT 為 Qt 預設的巨集 (macro) ,因此只要建立視窗物件都會先執行這個巨集。
最後看到有兩個成員函數 (member function)
public:
explicit EncryptWindow(QWidget *parent = nullptr);
~EncryptWindow();
建構函數 (constructor) EncryptWindow() 用 explicit 宣告,這是避免建立 EncryptWindow 物件 (object) 時進行隱性型態轉換,而另一個有 ~ 符號的則是解構函數 (destructor) ,這是物件結束時執行的函數 (function) ,這牽涉到記憶體管理的內容,下面我們先來討論一下記憶體管理囉!
相關教學影片
中英文術語對照 | |
---|---|
專案 | project |
類別 | class |
標頭檔 | header file |
前置處理器指令 | preprocessor directive |
編譯器 | compiler |
命名空間 | name space |
繼承 | inherit |
物件導向程式設計 | object-oriented programming |
子類別 | subclass |
父類別 | superclass |
成員 | member |
巨集 | macro |
成員函數 | member function |
建構函數 | constructor |
物件 | object |
解構函數 | destructor |
函數 | function |
重點整理 |
---|
1. 專案 encrypt_gui 中, M 是指 Encrypt 類別 (encrypt.cpp) , V 為 Qt Creator 自動產生的 encryptwindow.ui , C 則是 encryptwindow.cpp 。 |
2. Qt Creator 建立專案後,所有相關檔案都會放到同一個資料夾(目錄)中。 |
3. #ifndef 、 #define 、 #endif 等都是前置處理器指令。 |
4. 關鍵字 namespace 用來宣告命名空間。 |
5. 繼承是物件導向程式設計的特性之一,子類別可以承接父類別的 public 與 protected 成員。 |
6. Q_OBJECT 為 Qt 預設的巨集。 |
7. 相對於建構函數,具有 ~ 符號的稱為解構函數。 |
問題與討論 |
---|
1. 為什麼 Qt 的基本執行程式 main.cpp 中要建立 QApplication 物件? |
2. 巨集是什麼?為什麼 Qt 程式要先執行 Q_OBJECT 巨集? |
3. 除了 #ifndef 、 #define 、 #endif 外還有其他的前置處理器指令嗎? |
4. 為什麼要宣告命名空間 Ui ? |
5. 繼承的意義為何?除了繼承外,物件導向程式設計還有哪些特性? |
練習 |
---|
1. 承接單元 21 練習發展完成的陣列版 GuessGame 類別,重構成字串版,為此版本先設計標頭檔,將新程式寫在 exercise2501.h 中,記得先引進標準程式庫中的 string ,並將 a 、 b 改成資料成員。 |
2. 承上題,重新設計 set_game() ,將實作檔案寫在 exercise2501.cpp 中。 |