Новости

Фотки
Надписи на партах
Под гармошку
Обои для рабочего стола

Downloads
Компьютерные магазины
Фуфло

ЕЯС
НКС
ООП
СЦОД

Гостевая

Я ВКонтакте

Объектно-ориентированное программирование

Урок №1. Перемещение объекта.

Задача:

Создать программу, позволяющую пользователю перемещать некоторый объект на экране. Управление перемещением производится с помощью клавиатуры. Обеспечить графический режим.

Решение:

Итак, с выбором объекта мудрить не станем. Для простоты, будем перемещать квадрат. Разбёрёмся, какие данные требуются, чтобы однозначно задать квадрат. Пусть это будут координата левого верхнего угла квадрата и длина его стороны. Координата, в свою очередь, задаётся двумя компонентами x и y.

Можем написать следующий фрагмент:

class Kvadrat {
protected:
  int x, y, a;
};

Данные сделаем защищёнными от внешнего воздействия - поместим их в раздел private или protected (в данном случае protected).

Сделаем конструктор для данного класса, который будет инициализаровать член-данные x, y и a какими-то начальными значениями. Всегда нужно делать конструктор, который задаёт начальные значения для член-данных класса. Это требуется для того, чтобы уменьшить количество глюков в ваших программах. :)

Ежу понятно, что конструктор должен быть описан в разделе public, иначе как бы он вызывался при создании экземпляра класса.

class Kvadrat {
protected:
  int x, y, a;
public:
  Kvadrat (int x_, int y_, int a_) {
    x= x_;
    y= y_;
    a= a_;
  }
};

Не нравится мне эта запись. Перепишем вот так:

class Kvadrat {
protected:
  int x, y, a;
public:
  Kvadrat (int x_, int y_, int a_): x(x_), y(y_), a(a_) {}
};

Далее. Добавим в класс две функции, одна из которых будет рисовать квадрат на экране (show()), а другая - стирать его оттуда (hide()).

class Kvadrat {
protected:
  int x, y, a;
public:
  Kvadrat (int x_, int y_, int a_): x(x_), y(y_), a(a_) {}

  void show() {
    setcolor (WHITE);
    rectangle (x, y, x+a-1, y+a-1);
  }

  void hide() {
    setcolor (BLACK);
    rectangle (x, y, x+a-1, y+a-1);
  }
};

Ещё один момент, так как функции класса не должны изменять значения полей класса, в их описание желательно включить служебное слово const:

class Kvadrat {
protected:
  int x, y, a;
public:
  Kvadrat (int x_, int y_, int a_): x(x_), y(y_), a(a_) {}

  void show() const {
    setcolor (WHITE);
    rectangle (x, y, x+a-1, y+a-1);
  }

  void hide() const {
    setcolor (BLACK);
    rectangle (x, y, x+a-1, y+a-1);
  }
};

Спрашивается, для чего это всё нужно? Зачем эти области видимости private, protected, public? Для чего это слово "const" в конце заголовка функций?

Ответ: да без проблем! Делайте как хотите: можете при объявлении класса сразу сделать область видимости public, и тогда не возникнет никаких затруднений с доступом к членам класса. Можете, не употреблять const - и тогда можете не беспокоиться, что компилятор выдаст ошибку, если вы будете изменять член-данные класса в этой функции.

Просто всё это нужно для самогО программиста. Чтобы исключить свои же собственные ошибки. Чтобы самому себе ограничить возможности, как это ни парадоксально звучит. Вообще, чем меньше доступа к данным, тем лучше. На будущее: пишите все член-данные (переменные) - в разделах private или protected, а чтобы присваивать значения этим член-данным, в разделе public пишите соответствующие член-функциии. Это и называется страшным словом инкапсуляция. :)

Ладно, понимайте как хотите. Пойдём дальше.

Добавим функцию, которая может поместить квадрат в любое место экрана (put(x_,y_)). Принцип действия её будет таким: стереть старое изображение квадрата, поменять старые координаты на новые, нарисовать квадрат на новом месте.

class Kvadrat {
protected:
  int x, y, a;
public:
  Kvadrat (int x_, int y_, int a_): x(x_), y(y_), a(a_) {}

  void show() const {
    setcolor (WHITE);
    rectangle (x, y, x+a-1, y+a-1);
  }

  void hide() const {
    setcolor (BLACK);
    rectangle (x, y, x+a-1, y+a-1);
  }

  void put (int x_, int y_) {
    hide();
    x= x_;
    y= y_;
    show();
  }
};

Тут я подумал, и решил: сделаем-ка мы так, чтобы квадрат рисовался сразу при создании экземпляра класса и исчезал при удалении этого экземпляра. Для этого из тела конструктора будем вызывать функцию show(), а из тела деструктора - функцию hide(). Внимательный программер спросит, а где же я тут увидел деструктор. И я тут же ему отвечу: "Нигде. Деструктор мы сейчас создадим."

class Kvadrat {

protected:

  int x, y, a;

public:

  Kvadrat (int x_, int y_, int a_): x(x_), y(y_), a(a_) { show(); }

  ~Kvadrat () { hide(); }

  void show() const {
    setcolor (WHITE);
    rectangle (x, y, x+a-1, y+a-1);
  }

  void hide() const {
    setcolor (BLACK);
    rectangle (x, y, x+a-1, y+a-1);
  }

  void put (int x_, int y_) {
    hide();
    x= x_;
    y= y_;
    show();
  }
};

Блин, уже столько строчек написали, не терпится проверить функционирование своего творения. Переходим к функции main. Возьмём стандартную форму для графического режима. Для удобства графический драйвер EGAVGA.BGI поместим в тот же каталог, где находится программа. Тогда и путь к нему не придётся прописывать.

#include <graphics.h> #include <iostream.h>
void main () {

  int graphDriver= DETECT, graphMode;
  initgraph (&graphDriver, &graphMode, "");
  if (graphresult() != grOk) {
    cout << "Ошибка инициализации графического режима\n";
    return;
  }

  closegraph();
}

Теперь проведём небольшой тест на работоспособность. В программу добавим этот код:

getch();

Kvadrat *kvadrat= new Kvadrat (200, 200, 25);
getch();

kvadrat->hide();
getch();

kvadrat->show();
getch();

kvadrat->put (400, 100);
getch();

delete kvadrat;

В результате, программа примет следующий вид:

#include <graphics.h> #include <iostream.h>
class Kvadrat {

protected:

  int x, y, a;

public:

  Kvadrat (int x_, int y_, int a_): x(x_), y(y_), a(a_) { show(); }

  ~Kvadrat () { hide(); }

  void show() const {
    setcolor (WHITE);
    rectangle (x, y, x+a-1, y+a-1);
  }

  void hide() const {
    setcolor (BLACK);
    rectangle (x, y, x+a-1, y+a-1);
  }

  void put (int x_, int y_) {
    hide();
    x= x_;
    y= y_;
    show();
  }
};

void main () {

  int graphDriver= DETECT, graphMode;
  initgraph (&graphDriver, &graphMode, "");
  if (graphresult() != grOk) {
    cout << "Ошибка инициализации графического режима\n";
    return;
  }
  getch();

  Kvadrat *kvadrat= new Kvadrat (200, 200, 25);
  getch();

  kvadrat->hide();
  getch();

  kvadrat->show();
  getch();

  kvadrat->put (400, 100);
  getch();

  delete kvadrat;
  closegraph();
}

Взглянув на этот код, несложно догадаться как должна вести себя программа при выполнении: После запуска пользователь должен увидеть пустой экран, затем при нажатии клавиши (например "Пробел") должен появиться небольшой квадратик, при следующем нажатии он должен исчезнуть, при следующем - снова появиться, при следующем - квадратик должен переместиться в другое место, и при последнем нажатии - программа должна завершиться.

Можете проверить, я уже проверил.

После того, как мы убедились в работоспособности класса Kvadrat, начинаем думать про управление перемещением кварата.

Реализуем это следующим образом, добавим в класс Kvadrat четыре член-функции, которые будут уметь перемещать квадрат в четыре направления. Обзовём эти функции так: move_up(), move_left(), move_right(), move_down(). И пусть эти функции работают с функцией put(x_,y_):

void move_up() { put (x, y-10); }
void move_left() { put (x-10, y); }
void move_right() { put (x+10, y); }
void move_down() { put (x, y+10); }

Правда, оригинально придумано? :) Функция move_up() просто помещает квадрат на 10 пикселей выше, чем он был до этого, функция move_left() - на 10 пикселей левее, и т.д.

Теперь надо обеспечить вызов этих функций пользователем. Сделать это не сложно:

int quit= 0;

do {

  switch (getch()) {

    case 27:

      quit= 1;
      break;

    case 'w':

      kvadrat->move_up();
      break;

    case 'a':

      kvadrat->move_left();
      break;

    case 'd':

      kvadrat->move_right();
      break;

    case 's':

      kvadrat->move_down();
      break;
  }

} while (!quit);

В этом цикле постоянно будет считываться код нажатой клавиши, и этот код тут же будет рассматриваться в блоке switch. Как вы уже догадались управление будет производиться клавишами 'w', 'a', 'd', 's'. Никогда нельзя забывать о том, как вы будете выходить из программы. Для этого в блок switch включена обработка клавиши "Esc", код которой равен 27. По нажатию клавиши "Esc" переменной qiut будет присвоено значение 1, в результате чего условие продолжения цикла станет ложным.

Пожалуй стоит усовершенствовать этот блок до следующего вида:

int quit= 0;

do {

  switch (getch()) {

    case 27:

      quit= 1;
      break;

    case 'W':
    case 'w':
    case 'Ц':
    case 'ц':

      kvadrat->move_up();
      break;

    case 'A':
    case 'a':
    case 'Ф':
    case 'ф':

      kvadrat->move_left();
      break;

    case 'D':
    case 'd':
    case 'В':
    case 'в':

      kvadrat->move_right();
      break;

    case 'S':
    case 's':
    case 'Ы':
    case 'ы':

      kvadrat->move_down();
      break;
  }

} while (!quit);

А то мало ли что, вдруг окажется включенным Caps Lock, или язык клавиатуры будет сменён, или и то и другое.

Итак, программа у вас должна стать такой:

#include <conio.h> #include <graphics.h> #include <iostream.h>
class Kvadrat {

  protected:

    int x, y, a;

  public:

    Kvadrat (int x_, int y_, int a_): x(x_), y(y_), a(a_) {
      show();
    }

    ~Kvadrat() { hide(); }

    void show() const {
      setcolor (WHITE);
      rectangle (x, y, x+a-1, y+a-1);
    }

    void hide() const {
      setcolor (BLACK);
      rectangle (x, y, x+a-1, y+a-1);
    }

    void put (int x_, int y_) {
      hide();
      x= x_;
      y= y_;
      show();
    }

    void move_up() { put (x, y-10); }

    void move_left() { put (x-10, y); }

    void move_right() { put (x+10, y); }

    void move_down() { put (x, y+10); }
};

void main () {

  int graphDriver= DETECT, graphMode;
  initgraph (&graphDriver, &graphMode, "");
  if (graphresult() != grOk) {
    cout << "Graphics error.\n";
    return;
  }

  Kvadrat * kvadrat= new Kvadrat (200, 200, 15);

  int quit= 0;

  do {

    switch (getch()) {

      case 27:

        quit= 1;
        break;

      case 'W':
      case 'w':
      case 'Ц':
      case 'ц':

        kvadrat->move_up();
        break;

      case 'A':
      case 'a':
      case 'Ф':
      case 'ф':

        kvadrat->move_left();
        break;

      case 'D':
      case 'd':
      case 'В':
      case 'в':

        kvadrat->move_right();
        break;

      case 'S':
      case 's':
      case 'Ы':
      case 'ы':

        kvadrat->move_down();
        break;
    }

  } while (!quit);

  delete kvadrat;

  closegraph();
}

Что ж, с задачей мы справились, прямоугольник действительно двигается. Но, блин! Чё за фигня... Он же может перемещается за границы экрана!..

Впрочем, это легко исправить. Для этого добавим в функцию put(x_,y_) дополнительное условие:

void put (int x_, int y_) {
  if (x_>=&& y_>=&& x_+a-1<=getmaxx() && y_+a-1<=getmaxy()) {
    hide();
    x= x_;
    y= y_;
    show();
  }
}


Урок №1. Перемещение объекта.

Урок №2. Игра "Арканоид".

Урок №3. Обработка нажатий клавиш.

Урок №4. Системный таймер.

Урок №5. Немного о виртуальных функциях.

^^ наверх ^^

Дизайн: Красиков Виктор, kv630@mail.ru, ICQ - 319227
Местонахождение: Россия, респ. Бурятия, г. Улан-Удэ
Время на сервере: 02.04.23 03:33
Время в Улан-Удэ: 02.04.23 08:33
Время генерации страницы 0.0030019999999999 сек.