List of articles in this column
First, what is object-oriented
Second, C language can also achieve object-oriented
Third, the non-elegant features in C + +
Iv. solve the package and avoid the interface
V. Rational use of templates to avoid code redundancy
VI, C + + can also reflect
Vii. single-Case pattern solving the construction order puzzle of static member objects and global objects
Viii. more advanced preprocessor PHP
Ix. Best Practices for GTKMM
Ix. Best Practices for GTKMM
QT has always been a very popular GUI development framework in cross-platform GUI development, but QT one is dependent on reflection and requires special preprocessing steps, one of which is that the library is too large, which creates some inconvenient places. Today's introduction to everyone is the GTK + Library of C + + bindings, GTKMM, a convenient cross-platform GUI development framework.
Because it is a C + + package, GTK is no longer so difficult to use, become concise and elegant, and very efficient, compiling is much faster than QT.
Although C can also be written, and we have previously introduced the use of GObject. But it is more cumbersome to implement, the number of lines of code is more than C + +, and each member function to manually pass the this pointer, more inconvenient.
Now C + + is very compact, and very easy to write, and easy to use, if it is properly encapsulated and designed in accordance with previous design ideas.
Version 2048 of the GTKMM program
For better practice, let's cite a simple 2048-game program as an example. We will find that a reasonable design can make the code both clear and easy to maintain, high reliability.
We have a brief introduction to the program design, here we do not have to learn how to make 2048, but to understand the design of the idea, as well as the aesthetic and artistic sense in designing.
First of all, 2048 as a simple small game, widely liked by everyone, the principle is very simple, in a 4x4 array, let the numbers continue to merge in all directions, after each time, a random position to create a new number.
The program interface, a window, the above line of labels to write the current score, the following a drawing interface, free to draw, a 4x4 matrix, the above written content.
Program structure design, in accordance with the general program architecture design, you can use the structure of MVC, an interface class, responsible for display, a control class, responsible for game logic, a model class, responsible for data storage and management.
But because the management of the data is too simple, the model class is discarded, and the task is accomplished directly using a 4x4 matrix.
Program Practice
Because of the good encapsulation of GTKMM, we do not need too much complex processing, first of all, the boot of the main file boot, all GTK program collection is such boot.
/* * @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(intchar *argv[]){ "org.abs.gtk2048"); Game game; //Shows the window and returns when it is closed. return app->run(game);}
The game class, which is the core window class, is also the main control class for games and does not need to expose what methods are used for external members, so its definition is simple:
/* * @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;classpublic Gtk::Window{public: Game(); virtual ~Game();private: Game_private* priv;};#endif // GAME_H
This notation, which is the method of enhancing the encapsulation of the code mentioned in the previous chapters, solves the problem of imperfect C + + encapsulation through a priv pointer.
There is also a great advantage is that because the Priv pointer writing is more cumbersome, if in the public method, repeated through the Priv pointer to call the function, it will be extremely troublesome, but this is to remind you that your writing is problematic, because the general method, as far as possible to write the internal private, So you in the unconscious moment, the formation of the maximization of private methods, minimize the interface design habits, which to enhance the cohesion of the program, very meaningful.
The internal definition of our game class becomes very complex, but it makes the code more cohesive and exposes the interface to the outer layer to be simpler.
Class game_private{ Public:game_private(game* Game); ~game_private (); game* game; Myarea M_area;//Render classGtk::box M_box;//Layout controlsGtk::label M_score;//Score Tags intScore;//Score specific numbers Const Static intfx[4][2];intdata[4][4];//Data Model BOOLCombineintIintJintk);//Merge the number of a position down BOOLRandomnew ();//Randomly create new numbers voidCleandata ();//Delete all numbers for start-up initialization voidGamewin ();//Show user wins voidGameover ();//Show end of game voidGamerun (intk);//Game control Loop BOOLOn_key_press_event (gdkeventkey*Event);//Monitor keyboard event response};
I don't like the function that is longer than the face, but the function here is not so satisfactory, though, it is designed in a simple and understandable way.
Here the Combine method is very special, because when merging, there is the possibility of the game to win the situation, so it contains the criteria for judging the victory.
BOOLCombineintIintJintK) {intNI = i;intNJ = j; NI + = fx[k][0];//FX is an array of directionsNJ + = fx[k][1];intObji = i, objj = j; while(0<= ni && ni <4&&0<= NJ && NJ <4)//Prevent cross-border, I'm here to compare cheap, many people deal with the cross-border by adding a special value outside the array outside the box to handle the{if(Data[ni][nj] = =0) {Obji = ni; objj = NJ; }Else{if(Data[ni][nj] = = Data[i][j]) {Score + = (1<< Data[ni][nj]);//Processing score++DATA[NI][NJ]; DATA[I][J] =0;if(Data[ni][nj] = = A)return true;//Processing victory conditions return false; }Else Break; } ni + = fx[k][0]; NJ + = fx[k][1]; }if(! (Obji = = I && objj = = j)) {//Failure to mergeDATA[OBJI][OBJJ] = Data[i][j]; DATA[I][J] =0; }return false;}
This function is designed to be robust, taking into account many boundary conditions, and this is the case when simulating an object collision, the collision surface is constantly squeezed. For example, the following conditions:
1 1 2 4
0 0 0 0
0 0 0 0
0 0 0 0
Merging to the left can be synthesized to 8 at a time, but it is also inseparable from the merge order control of the outer layer.
In the game main loop control, this is handled, for different directions, the loop order is not the same:
voidGamerun (intK) {BOOLWinflag =false;Switch(k) { Case 0: for(inti =0; I <4; ++i) for(intj =0; J <4; ++J)if(Combine (i,j,k)) Winflag =true; Break; Case 1: for(intj =3; J >=0; --J) for(inti =0; I <4; ++i)if(Combine (i,j,k)) Winflag =true; Break; Case 2: for(inti =3; I >=0; I.) for(intj =0; J <4; ++J)if(Combine (i,j,k)) Winflag =true; Break; Case 3: for(intj =0; J <4; ++J) for(inti =0; I <4; ++i)if(Combine (i,j,k)) Winflag =true; Break; }//Judging the outcome conditions 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, -, -); Win->invalidate_rect (R,false); M_area.show ();Charscore_text[ -];memset(Score_text,0, -);sprintf(Score_text,"Score:%d", score); M_score.set_text (Score_text); } }
The way to create new numbers is also clear, but this is done using a simulated stack.
Design is very unique, due to the current limited number of positions, direct Rand's way, efficiency is low, we first scan all possible locations, and then put it into the stack, random, directly find a location, and then directly randomly from the one to find it. The method of array simulation is mainly to avoid the low efficiency of vectors and to realize the simplicity of the stack. And the extension is more convenient, if you want to random and more, the modification is also very convenient.
BOOL Randomnew () {intempty_block[ -][2];int sum=0; for(inti =0; I <4; ++i) { for(intj =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;intt = 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;}
Rendering class is very simple, mainly based on the values in the array to render the corresponding image, the design idea is to constantly abstract the problem, continue to simplify, the complex problem from the upper layer of the layer, so that makes the structure more concise and elegant.
The entire project's complete code has been put on GitHub and needs to be consulted:
"GitHub Warehouse"
Object-oriented theme for C and C + + (9) Best Practices for--GTKMM