Ciąg dalszy wykorzystania mechanizmów obiektowych w przykładzie wykorzystującym klasę Runner. Prosty program prezentujący klasę Runner wyposażoną w konstruktor. Klasa wykorzystuje hermetyzację, jej pola zostały uprywatnione i wyposażone w odpowiednie funkcje dostępowe set/get.
Program stanowi uzupełnienie wykładu z programowania w językach C/C++. Koncepcja klasy Runner została omówiona we wprowadzeniu do programowania obiektowego (pdf).
Do pobrania wersja źródłowa pliku (main02.cpp).
Poprzednia wersja: main02.cpp.
Uwaga! Implementacja tylko dla Windows, obsługa konsoli realizowana za pośrednictwem WinApi. Najlepiej kompilować używając MinGW (środowiska Code::Blocka, DevC++) lub kompilatorów w środowisku C++ Builder. Proszę unikać środowisk VisualC++, CLion, QT, będą stwarzać problemy.
// Runner, wersja 02 #include <cstdlib> #include <cstring> #include <cstdio> #include <ctime> #include <conio.h> #if defined(__MINGW32__) || defined(_MSC_VER) #include <windows.h> #endif // Kody klawiszy sterujących kursorem i klawiszy specjalnych enum KEY_CODES { #ifdef __BORLANDC__ KEY_BLANK = 0x0000, #elif (defined(__GNUC__) && defined(__MINGW32__)) || defined(_MSC_VER) KEY_BLANK = 0x00e0, #endif KEY_UP = 0x4800, KEY_DOWN = 0x5000, KEY_LEFT = 0x4b00, KEY_RIGHT = 0x4d00, KEY_ESC = 0x001b }; // Prototypy funkcji obsługi konsoli int getKey(); void clearScreen(); void writeCharXY(int x, int y, char c); void writeStrXY(int x, int y, char s[]); void writeIntXY(int x, int y, int i); void writeDoubleXY(int x, int y, double d); void cursorOff(); void cursorOn(); // Rozmiary ekranu konsoli, nie uwzglêdniają ostatniego wiersza // ten jest wierszam statusy i zawiera informacje o stanie gry const int NUM_OF_COLS = 80; const int NUM_OF_ROWS = 24; // Klasa Runner z polami prywatnymi. Domyślna pozycja startowa to (1, 1), // nie pozwalamy na ustawienie pozycji < 1 i odpowiednio > NUM_OF_COLS, // NUM_OF_ROWS. Próba ustawienia nieprawidłowej parametrów skutkuje // ustwieniem wartości domyślnych (1, 1, '*'). class Runner { public: Runner(int startX = 1, int startY = 1, char startShape = '*'); // "Ustawiacze" void setX(int newX); void setY(int newY); void setShape(char newShape); // "Pobieracze" int getX(); int getY(); char getShape(); // "Realizatory" void show(); void hide(); void moveUp(); void moveDown(); void moveLeft(); void moveRight(); private: int x, y; char shape; // Pomocnicze funkcje weryfikacji pozycji ekranowej bool isXOnScreen(int x); bool isYOnScreen(int y); }; // Definicje funkcji składowych poza deklaracją klasy Runner::Runner(int startX, int startY, char startShape) { // Wartości x, y i shape mogłyby być ustawione na liście inicjalizacyjnej. // Ale wartości paramterów konstruktora mogły być nieprawidłowe, dlatego // są ustawione w ciele konstruktora z wykorzystaniem odpowiednich funkcji // ustawiających setX(startX); setY(startY); setShape(startShape); } //// Trochę inna wersja konstruktora, wykorzystująca listę //// inicjalizacyjną. Do kontroli poprawności wykorzystywany jest //// trójargumentowy operator warunkowy. Ta wersja kłóci się //// z zasadą Single Responsibility (SOLID), weryfikacja i ustalanie //// wartości domyśłnych jest elementem odpowiednich funkcji set..., //// i to raczej one powinny być wykorzystywane //Runner::Runner(int startX, int startY, char startShape) //: x(isXOnScreen(startX) ? startX : 1), // y(isYOnScreen(startY) ? startY : 1), // shape((startShape> 32 && startShape<= 127) ? startShape : '*') //{ //} void Runner::setX(int newX) { x = isXOnScreen(newX) ? newX : 1; // Zamiast: // if(isXOnScreen(newX)) // x = newX; // else // x = 1; } void Runner::setY(int newY) { y = isYOnScreen(newY) ? newY : 1; } void Runner::setShape(char newShape) { shape = (newShape > 32 && newShape <= 127) ? newShape : '*'; } void Runner::show() { writeCharXY(x, y, shape); } void Runner::hide() { writeCharXY(x, y, ' '); } int Runner::getX() { return x; } int Runner::getY() { return y; } char Runner::getShape() { return shape; } void Runner::moveUp() { hide(); if(y > 1) --y; show(); } void Runner::moveDown() { hide(); if(y < NUM_OF_ROWS) ++y; show(); } void Runner::moveLeft() { hide(); if(x > 1) --x; show(); } void Runner::moveRight() { hide(); if(x < NUM_OF_COLS) ++x; show(); } bool Runner::isXOnScreen(int newX) { return (newX > 0 && newX <= NUM_OF_COLS); } bool Runner::isYOnScreen(int newY) { return (newY > 0 && newY <= NUM_OF_ROWS); } int main() { int key; Runner R; R.setX(40); R.setY(12); R.setShape('*'); cursorOff(); clearScreen(); R.show(); do { switch( key = getKey() ) { case KEY_UP : R.moveUp(); break; case KEY_DOWN : R.moveDown(); break; case KEY_LEFT : R.moveLeft(); break; case KEY_RIGHT : R.moveRight(); break; }//switch } while( key != KEY_ESC ); clearScreen(); cursorOn(); return EXIT_SUCCESS; } // Funkcje obsługi konsoli, sprawa techniczna, w sensie merytorycznym nieistotna int getKey() { int key = _getch(); return (key == KEY_BLANK) ? _getch() << 8 : key; } void clearScreen() { #ifdef __BORLANDC__ clrscr(); #elif (defined(__GNUC__) && defined(__MINGW32__)) || defined(_MSC_VER) COORD leftTop = { 0, 0 }; CONSOLE_SCREEN_BUFFER_INFO consoleInfo; int numOfCells = 80 * 25; DWORD writtenItems; HANDLE consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE); if (GetConsoleScreenBufferInfo(consoleHandle, &consoleInfo)) numOfCells = consoleInfo.dwSize.X * consoleInfo.dwSize.Y; FillConsoleOutputAttribute(consoleHandle, 0xf, numOfCells, leftTop, &writtenItems); FillConsoleOutputCharacter(consoleHandle, ' ', numOfCells, leftTop, &writtenItems); #else #error "Nieobslugiwana platforma" #endif } void writeCharXY(int x, int y, char c) { #ifdef __BORLANDC__ gotoxy(x, y); putch(c); #elif (defined(__GNUC__) && defined(__MINGW32__)) || defined(_MSC_VER) COORD cursorPos; DWORD written; cursorPos.X = x - 1; cursorPos.Y = y - 1; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), cursorPos); WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), &c, 1, &written, 0); #else #error "Nieobslugiwana platforma" #endif } void writeStrXY(int x, int y, char s[]) { #ifdef __BORLANDC__ gotoxy(x, y); cputs(s); #elif (defined(__GNUC__) && defined(__MINGW32__)) || defined(_MSC_VER) COORD cursorPos; DWORD written; cursorPos.X = x - 1; cursorPos.Y = y - 1; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), cursorPos); WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), s, strlen(s), &written, 0); #else #error "Nieobslugiwana platforma" #endif } void writeIntXY(int x, int y, int i) { char s[80]; #if defined(_MSC_VER) sprintf_s(s, 80, "%d", i); #else sprintf(s, "%d", i); #endif writeStrXY(x, y, s); } void writeDoubleXY(int x, int y, double d) { char s[80]; #if defined(_MSC_VER) sprintf_s(s, 80, "%g", d); #else sprintf(s, "%g", d); #endif writeStrXY(x, y, s); } void cursorOff() { #ifdef __BORLANDC__ _setcursortype(_NOCURSOR); #elif (defined(__GNUC__) && defined(__MINGW32__)) || defined(_MSC_VER) CONSOLE_CURSOR_INFO cursorInfo; HANDLE consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE); GetConsoleCursorInfo(consoleHandle, &cursorInfo); cursorInfo.bVisible = false; SetConsoleCursorInfo(consoleHandle, &cursorInfo); #else #error "Nieobslugiwana platforma" #endif } void cursorOn() { #ifdef __BORLANDC__ _setcursortype(_NORMALCURSOR); #elif (defined(__GNUC__) && defined(__MINGW32__)) || defined(_MSC_VER) CONSOLE_CURSOR_INFO cursorInfo; HANDLE consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE); GetConsoleCursorInfo(consoleHandle, &cursorInfo); cursorInfo.bVisible = true; SetConsoleCursorInfo(consoleHandle, &cursorInfo); #else #error "Nieobslugiwana platforma" #endif }