C和C++的物件導向專題(9)——Gtkmm的最佳實務

來源:互聯網
上載者:User

標籤:c++   物件導向   開發   gtkmm   2048   


本專欄文章列表

一、何為物件導向

二、C語言也能實現物件導向

三、C++中的不優雅特性

四、解決封裝,避免介面

五、合理使用模板,避免代碼冗餘

六、C++也能反射

七、單例模式解決靜態成員對象和全域對象的構造順序難題

八、更為進階的前置處理器PHP

九、Gtkmm的最佳實務

九、Gtkmm的最佳實務

在跨平台的gui開發中,Qt一直是非常受歡迎的GUI開發架構,但Qt一個是依賴反射,需要特殊的預先處理步驟,一個是庫太過龐大,這就造成了一些不便的地方。今天介紹給大家的是Gtk庫的C++綁定,Gtkmm,一個方便的跨平台GUI開發架構。

由於是C++的封裝,GTK不再那麼的難以使用,變得簡潔優雅,而且效率非常高,編譯也較QT快許多。
雖然C也能編寫,而且我們之前也介紹過了GObject的使用。但比較其實現起來較為繁瑣,程式碼數較C++多一些,而且每個成員函數都要手動傳入this指標,較為不便。

現在C++如果合理的封裝和按照之前的設計思想進行設計,結構十分緊湊,而且書寫非常方便,非常易用。

Gtkmm版的2048程式設計

為了更好的實踐,我們舉一個簡單的2048小遊戲的程式作為執行個體。大家會發現,合理的設計,能夠使代碼既清晰明了,又方便維護,可靠性很高。
我們簡要的進行一下程式設計,這裡我們不是要學會2048如何製作,而是要體會程式設計中的思想,以及設計中的美感和藝術感。

首先,2048作為一個簡單的小遊戲,廣受大家喜歡,原理很簡單,在一個4×4的數組中,讓數字不斷向各個方向合并,每次進行後,隨機位置建立新數字。

程式介面,一個視窗,上面一列標籤書寫當前得分,下面一個繪圖介面,自由繪圖,畫上4×4個的矩陣,上面書寫內容。

程式結構設計,按照一般程式架構設計,可以用MVC的結構,一個介面類,負責顯示,一個控制類,負責遊戲邏輯,一個模型類,負責資料的儲存與管理。

但由於資料的管理太過簡單,就放棄了模型類,直接使用一個4×4的矩陣就完成任務。

程式實踐

由於Gtkmm的良好封裝,我們並不需要太多複雜的處理,首先是Main檔案引導程式的啟動,所有gtk程式集合都是這樣引導。

/* * @Author: sxf* @Date:   2015-05-07 12:22:40* @Last Modified by:   sxf* @Last Modified time: 2015-05-20 21:58:16*/#include <gtkmm.h>#include "game.h"int main(int argc, char *argv[]){    Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.abs.gtk2048");    Game game;    //Shows the window and returns when it is closed.    return app->run(game);}

Game類作為最核心的視窗類別,也是遊戲的主要控制類,並不需要暴露什麼方法給外部成員使用,所以它的定義很簡單:

/* * @Author: sxf* @Date:   2015-05-19 11:20:42* @Last Modified by:   sxf* @Last Modified time: 2015-05-20 21:58:35*/#ifndef GAME_H#define GAME_H#include <gtkmm/window.h>class Game_private;class Game : public Gtk::Window{public:    Game();    virtual ~Game();private:    Game_private* priv;};#endif // GAME_H

這種寫法,就是在前幾章提到的增強代碼封裝性的方法,通過一個priv指標,解決了C++封裝不完善的問題。

這樣還有一個很大的好處就是,由於priv指標的書寫較為繁瑣,如果在public方法中,反覆的通過priv指標調用函數,就會顯得無比麻煩,但這正提醒你,你的寫法有問題,因為一般的方法,要儘可能寫成內部的private的,這樣你在不自覺的時刻,就形成了最大化private方法,最小化介面的設計習慣,這對於提升程式的內聚性,很有意義。

於是我們的Game類的內部定義就變得十分複雜,但這就使得代碼內聚性更高,暴露給外層的介面就更簡單。

class Game_private{public:    Game_private(Game* game);    ~Game_private();    Game* game;    MyArea m_area;      // 渲染類    Gtk::Box m_box;     // 布局控制項    Gtk::Label m_score; // 得分標籤    int score;          // 得分具體數字    const static int fx[4][2];    int data[4][4];     // 資料模型    bool combine(int i, int j, int k);  // 將一個位置的數字向下合并    bool randomNew();   // 隨機建立新數字    void cleanData();   // 刪除全部數字,用來開局初始化    void gameWin();     // 顯示使用者勝利    void gameOver();    // 顯示遊戲結束    void gameRun(int k);    // 遊戲控制迴圈    bool on_key_press_event(GdkEventKey* event); // 監聽鍵盤事件響應};

我不喜歡比臉還長的函數,但這裡的函數設計的還是不是那麼盡如人意,雖然如此,這裡也是本著簡單易懂的方式設計的。

這裡的combine方法設計的很特殊,因為合并時,還有可能出現遊戲勝利的情況,所以裡麵包含了判斷勝利的條件。

bool combine(int i, int j, int k) {    int ni = i; int nj = j;    ni += fx[k][0]; // fx是方向數組    nj += fx[k][1];    int obji = i, objj = j;    while ( 0 <= ni && ni < 4 &&            0 <= nj && nj < 4 )         // 防止越界,我這裡比較貪便宜,很多人處理越界是通過在數組外增加一個特殊值的外邊框來處理的    {        if (data[ni][nj] == 0) {            obji = ni; objj = nj;        } else {            if (data[ni][nj] == data[i][j]) {                score += (1 << data[ni][nj]);   // 處理得分                ++data[ni][nj];                data[i][j] = 0;                if (data[ni][nj] == 12) return true; // 處理勝利條件                return false;            } else break;        }        ni += fx[k][0];        nj += fx[k][1];    }    if (!(obji == i && objj == j)) {    // 未能合并的情況        data[obji][objj] = data[i][j];        data[i][j] = 0;    }    return false;}

這個函數設計的很健壯,考慮了許多邊界條件,這麼做是在類比物體碰撞時,碰撞面不斷擠壓的情況。例如下面的情況:
1 1 2 4
0 0 0 0
0 0 0 0
0 0 0 0
向左合并,能夠一次就被合成為8,但這也是和外層的合并順序控制是分不開的
在遊戲主迴圈控制時,是這樣處理的,對於不同的方向,迴圈順序是不一樣的:

void gameRun(int k) {        bool winflag = false;        switch (k) {            case 0 :                 for (int i = 0; i < 4; ++i)                    for (int j = 0; j < 4; ++j)                        if (combine(i,j,k)) winflag = true;            break;            case 1 :                 for (int j = 3; j >= 0; --j)                    for (int i = 0; i < 4; ++i)                        if (combine(i,j,k)) winflag = true;            break;            case 2 :                 for (int i = 3; i >= 0; --i)                    for (int j = 0; j < 4; ++j)                        if (combine(i,j,k)) winflag = true;            break;            case 3 :                 for (int j = 0; j < 4; ++j)                    for (int i = 0; i < 4; ++i)                        if (combine(i,j,k)) winflag = true;            break;        }        // 判斷勝負條件        if (winflag) {            gameWin();            return;        }        if (!randomNew()) {            gameOver();        }        Glib::RefPtr<Gdk::Window> win = game->get_window();        if (win)        {            m_area.setData(data);            Gdk::Rectangle r(0, 0, 600, 600);            win->invalidate_rect(r, false);            m_area.show();            char score_text[20]; memset(score_text, 0, 20);            sprintf(score_text, "Score : %d", score);            m_score.set_text(score_text);        }    }

而建立新數位方式也很清晰,但這裡使用類比棧的方式進行了處理。
設計很獨特,由於目前的位置數目有限,直接rand的方式,效率較低,我們先掃描所有可能的位置,然後將其入棧,random時,直接找一個位置,然後直接隨機從其中找一個就可以了。數組類比棧的方式,主要是希望避免vector的低效率,實現較簡易。而且擴充較方便,如果你想random跟多,修改起來也十分方便。

bool randomNew() {    int empty_block[17][2]; int sum = 0;    for (int i = 0; i < 4; ++i) {        for (int j = 0; j < 4; ++j) {            if (data[i][j] == 0) {                empty_block[sum][0] = i;                 empty_block[sum][1] = j;                ++sum;            }        }    }    if (sum < 1) return false;    int t = rand() % sum;    data[ empty_block[t][0] ][ empty_block[t][1] ] = (rand() % 4) < 3 ? 1 : 2;    empty_block[t][0] = empty_block[sum-1][0];    empty_block[t][1] = empty_block[sum-1][1];     --sum;    return true;}

渲染類十分簡單,主要就是根據數組中的數值,渲染出對應的映像,設計思想就是不斷將問題抽象,不斷簡化,將複雜的問題從上層一層層撥開,這樣就使得結構更加簡潔優雅。

整個項目完整代碼已經放到Github上了,需要的可以參考:
【github倉庫】

C和C++的物件導向專題(9)——Gtkmm的最佳實務

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.