ООП и перегрузка операторов в С/C++

Ссылки для скачивания:

Отчёты по лабораторным должны содержать:

Для реализации программы можете использовать любой компилятор / IDE C/C++  на ваш выбор (я рекомендую MinGW + Eclipse).

1. Классы. Протоколы классов

1) Определить класс дробей – рациональных чисел, являющихся отношением двух целых чисел. Специфицировать интерфейс класса и реализовать методы сложения, вычитания, умножения и деления дробей. После выполнения метода дробь должна приводиться к несократимому виду.

2) В разработанном классе рациональных дробей заменить методы операций над заданными объектами перегруженными стандартными операторами. Определить операции сравнения «==», «>», «<», «>=», «<=» и «!=».

// Пример простейшей реализации 
#include <iostream>

#define show(x) #x << " = " << x << "  "
#define test(x) if(x) cout << #x << endl

// Вычисление НОД = Наибольший общий делитель
// GCD = Greatest Common Divider (по-английски)
long GCD( long a, long b ){ return b==0 ? a : GCD(b,a%b); }

// Класс "Рациональная дробь"
class Rat {
  void init( long n, long d ){
    long gcd = GCD(n,d); // вычисляем наибольший общий делитель числителя и знаменателя
    // Сокращаем дробь 
    this->n = n/gcd; 
    this->d = d/gcd; 
    curObj++; 
    constCalls++; 
  };
public:
  static int curObj /* текущее кол-во объектов */, constCalls /* кол-во вызовов конструкторов */; 
  long n,d; // Числитель и знаменатель nominator/denominator 
  Rat(long n, long d) { init(n,d); } // "Основной" конструктор
  Rat(const Rat& x) { init(x.n,x.d); }; // Конструктор копирования
  Rat() { init(0,1); } // Конструктор "по-умолчанию", дробь равная 0 - 0/1
  ~Rat() { curObj--; } // Деструктор (уменьшает количество объектов)
  // Арифметические операции
  Rat operator+(const Rat& x) const { return Rat( n*x.d+d*x.n, d*x.d ); }
  Rat operator-(const Rat& x) const { return Rat( n*x.d-d*x.n, d*x.d ); }
  Rat operator*(const Rat& x) const { return Rat( x.n*n, x.d*d ); }
  Rat operator/(const Rat& x) const { return Rat( n*x.d, d*x.n ); }
  // Операции сравнения
  bool operator==(const Rat& x) const { return n*x.d == d*x.n; }
  bool operator!=(const Rat& x) const { return n*x.d != d*x.n; }
  bool operator>(const Rat& x) const { return n*x.d > d*x.n; }
  bool operator<(const Rat& x) const { return n*x.d < d*x.n; }
  bool operator>=(const Rat& x) const { return n*x.d >= d*x.n; }
  bool operator<=(const Rat& x) const { return n*x.d <= d*x.n; }
};

// Инициализируем статические переменные
int Rat::curObj = 0, Rat::constCalls = 0;

using namespace std;

// Перегрузка операции вывода в поток для класса "Рациональная дробь"
ostream& operator << ( ostream& os, const Rat& x ){ return os << x.n << "/" << x.d; };

// Основная программа - демонстрация возможностей класса "Рациональная дробь"
void demoRat(){
  const Rat a(3,9),b(1,6),c = a*b+b/a;
  cout << show(a) << show(b) << endl;
  cout << show(a+b) << show(a-b) << show(a*b) << show(a/b) << endl;
  test(a==b); test(a!=b); test(a>b); test(a<b); test(a>=b); test(a<=b); test( a*b == b*a );
}

int main() {
  demoRat();
  cout << "Объектов в памяти = " << Rat::curObj << "   Вызовов конструкторов = " << Rat::constCalls << endl;
  return 0;
};

2. Перегрузка операторов

Разработать и реализовать класс «Строка» произвольной длины. Протокол класса должен включать конструктор копирования, метод вывода на экран, перегруженные операторы присваивания («=»), сложения («+» и «+=») и удаления подстроки («-» и «-=»), а также оператор преобразования строки в char*.

// Пример простейшей реализации
#include <assert.h>
#include <cstring>
#include <iostream>

using namespace std;

// Класс "Строка"
class String {
  char *p; // строка = указатель на массив символов
  int len; // длина строки
  void init(const char *s, int _len){
    len = _len; 
    p = new char[len+1]; 
    strcpy(p,s); 
  }
public:
  String(const char *s){ init(s,strlen(s)); };
  String(const String &x){ init(x.p,x.len); } // Конструктор копирования
  ~String(){ delete []p; }
  operator char*(){ return p; }; // оператор преобразования строки в char*
  String& operator=(String &x){ 
    delete []p; // Очищаем старую строку
    init(x.p,x.len); 
    return *this; 
  }
  // Конкатенация
  String& operator+=(String &x){ 
    char *old=p; 
    init(old,len+x.len); 
    strcat(p,x.p); 
    delete []old; 
    return *this; 
  };
  String& operator-=(String &x){ // удаление подстроки
    char* substr = strstr(p, x.p); // поиск подстроки
    if (substr != NULL) { // если подстрока найдена
      len -= x.len; char* pn = new char[len+1]; // создаём новую строку, которая короче на длину подстроки
      strncpy(pn, p, substr - p); strcpy(pn + (substr - p), substr + x.len); // копируем начало строки (до подстроки) и конец (после подстроки)
      delete []p; p = pn; // заменяем строку на новую
    }
    return *this; // возвращаем "указатель на себя"
  }
  String& operator+(String &x){ return *(new String(p))+=x; }
  String& operator-(String &x){ return *(new String(p))-=x; }
  // Перегрузка операции "<<" вывода в поток
  ostream& operator << (ostream& os){ return os << p; }
};
 
// Основная программа - демонстрация возможностей класса "Строка"
int main() {
  // Проверяем работу конкатенации
  String a("prog"), b("systema"), c = a + b; 
  assert(!strcmp(c,"progsystema"));
  // Проверяем работу второго варианта конкатенации
  a += b;   
  assert(!strcmp(a,"progsystema"));
  String d = a - *(new String("sys")); 
  assert(!strcmp(d,"progtema"));
  cout << d << endl;
  return 0;
}

4. Преобразования типов. Дружественные методы и классы

Разработать класс «Матрица целых чисел» произвольной размерности, использующий в качестве свойства двойной указатель int**. Протокол класса должен содержать необходимые конструкторы, деструктор и операции «=», «+», «-» (унарный и бинарный), «*» и «~» (транспонирование). Определить операцию индексирования [] как выделения матрицы строки (или столбца).

Определить в классе операции ввода «>>» и вывода «<<» через объекты классов istream и ostream.

#include <cassert>
#include <iostream>

using namespace std;

// Матрица целых чисел
class matrix {
  int rows,cols; // rows - количество строк, cols - количество столбцов
  int **data; // Данные матрицы
  // Инициализация матрицы 
  //   rows - количество строк
  //   cols - количество столбцов
  //   fillZero - надо ли заполнять нулями
  void init(int rows, int cols, bool fillZero){
    // Запоминаем количество строк и столбцов
    this->rows = rows;
    this->cols = cols;
    // Создаём массив указателей на int
    data = new int*[rows];
    // Создаём массивы int'ов и запоминаем указатели
    for(int i=0;i<rows;i++){
      data[i] = new int[cols];
      if(fillZero) // заполняем нулями если надо
        for(int j=0;j<cols;j++)
          data[i][j] = 0;
    }   
  }
public:
  matrix(){ init(0,0,true); }  // Конструктор без параметров
  matrix( int rows, int cols ){ init(rows,cols,true); }  // Основной конструктор
  // Конструктор копирования
  matrix(const matrix& m) { 
    init(m.rows,m.cols,false);
    for(int i=0;i<rows;i++)
      for(int j=0;j<cols;j++)
        data[i][j] = m.data[i][j];
  }
  // Деструктор
  ~matrix(){
    // Удаляем каждую строку
    for(int i=0;i<rows;i++)
      delete[] data[i];
    // Удаляем массив указателей на double
    delete[] data;
  }
  // Присваивание: m - копия, обмениваемся с ней данными
  matrix& operator =(matrix m) {
    swap(rows, m.rows);
    swap(cols, m.cols);
    swap(data, m.data);
    return *this;
  }
  int Rows() const { return rows; }  // Количество строк
  int Cols() const { return cols; }  // Количество столбцов
  // Операция выделения строки матрицы
  int* operator[] (int index) const { return data[index]; }
  // Сложение
  matrix& operator +=(const matrix& m) {
    // Для сложения матрицы должны быть одинакового размера
    assert( rows == m.rows && cols == m.cols );
    // Сладываем матрицы поэлементно
    for(int i=0; i<rows; i++)
      for(int j=0; j<cols; j++)
        data[i][j] += m.data[i][j];
    return *this;   
  }
  // Дружественная функция сложения 
  friend matrix operator +(const matrix& a,const matrix& b) { 
    matrix temp = a; 
    return temp += b; 
  }
  // Вычитание
  matrix& operator -=(const matrix& m) {
    assert( rows == m.rows && cols == m.cols );
    for(int i=0;i<rows;i++)
      for(int j=0;j<cols;j++)
        data[i][j] -= m.data[i][j];
    return *this;       
  }
  // Дружественная функция вычитания 
  friend matrix operator -(const matrix& a,const matrix& b) { 
    matrix temp = a; 
    return temp -= b; 
  }
  // Умножение - легче сделать бинарный оператор
  matrix& operator *=(const matrix& m) {
    assert( rows == m.cols && cols == m.rows );
    matrix temp(rows,m.cols);
    for(int i=0;i<rows;i++)
      for(int j=0;j<m.cols;j++){
        temp.data[i][j] = 0;
        for(int k=0;k<cols;k++)
          temp.data[i][j] += data[i][k] * m.data[k][j];
      }
    *this = temp;
    return *this;       
  }
  friend matrix operator *(const matrix& a,const matrix& b) {
    matrix temp = a;
    return temp *= b;
  }
  // Транспонирование
  matrix& operator ~() {
    matrix temp(cols,rows);
    for(int i=0;i<rows;i++)
      for(int j=0;j<cols;j++)
        temp.data[j][i] = data[i][j];       
    *this = temp;
    return *this;       
  }
  // Вывод в поток
  ostream& print(ostream &os){
    os << "(" << rows << "," << cols << ")" << endl;
    for(int i=0;i<rows;i++){
      for(int j=0;j<cols;j++)
        os << data[i][j] << " ";
      os << endl;
    }
    return os;
  }
  // Ввод из потока (в частности - с клавиатуры)
  istream& scan(istream &is){
    if(&is == &cin) cout << "Введите количество строк и столбцов: ";
    int r = 0, c = 0;
    is >> r >> c;
    *this = matrix(r,c); // матрица нужного размера
    for(int i=0;i<rows;i++)
      for(int j=0;j<cols;j++){
        if(&is == &cin) cout << "[" << i << "][" << j << "] = ";
        is >> data[i][j];
      }
    return is;
  }
};
 
// Оператор вывода в поток 
template <class T> ostream& operator << (ostream& os, T& t){
  return t.print(os);
}
 
// Оператор ввода из потока
template <class T> istream& operator >> (istream& is, T& t){
  return t.scan(is);
}
 
// Тестирование класса Матрица
int main(){
  const matrix m(2,1);
  assert( m.Rows() == 2 && m.Cols() == 1 );
  m[0][0] = 1; assert( m[0][0] == 1 );
  m[1][0] = 2; assert( m[1][0] == 2 );
  // Создаём 2 матрицы
  const matrix a(2,2);
  a[0][0] = 3; a[0][1] = 5;
  a[1][0] = 4; a[1][1] = 2;
  matrix b(2,2);
  b[0][0] = 2; b[0][1] = 1;
  b[1][0] = 1; b[1][1] = 3;
  // Используем конструктор копирования
  matrix c = b, c1 = b, c2 = b;
  assert( c[0][0] == 2 ); assert( c[0][1] == 1 );
  assert( c[1][0] == 1 ); assert( c[1][1] == 3 );
  // Сложение
  b+=a;
  assert( b[0][0] == 3+2 ); assert( b[0][1] == 5+1 );
  assert( b[1][0] == 4+1 ); assert( b[1][1] == 2+3 );
  matrix d = a+c;
  assert( d[0][0] == 3+2 ); assert( d[0][1] == 5+1 );
  assert( d[1][0] == 4+1 ); assert( d[1][1] == 2+3 );
  // Вычитание
  c-=a;
  cout << c << endl;
  assert( c[0][0] == 2-3 ); assert( c[0][1] == 1-5 );
  assert( c[1][0] == 1-4 ); assert( c[1][1] == 3-2 );
  matrix e = c1-a;
  assert( e[0][0] == 2-3 ); assert( e[0][1] == 1-5 );
  assert( e[1][0] == 1-4 ); assert( e[1][1] == 3-2 );
  // Умножение
  c2 *= a;
  // Надо протестировать умножение
  //  ...
  // Ввод матрицы с клавиатуры
  matrix f;
  cin >> f;
  // Вывод её же на экран
  cout << f << endl;
  return 0;
}

5. Одиночное наследование. Иерархия классов

Разработать иерархию классов «Линия», «Луч», «Отрезок». Реализовать методы нарисовать, сдвинуть, повернуть (использовать графический режим).

6. Одиночное наследование. Виртуальные функции

Разработать иерархию классов «Квадрат», «Круг», «Прямоугольник», «Фигура», «Эллипс». В класс «Экран» должен включать любую композицию из объектов указанных классов и уметь рисовать себя (использовать абстрактные классы).

7. Множественное наследование

Разработать иерархию и протоколы классов «квадрат», «ромб», «прямоугольник» и «параллелограмм». Определить методы для изменения размеров, расчета периметра и площади фигур.

8. Шаблоны классов

Создать шаблоны классов с необходимым для работы с объектами протоколом:

         А) одномерный массив произвольной длины,
         Б) список,
         В) стек,
         Д) кольцевой буфер произвольной длины.

Темы курсовых работ по курсу: «Объектно-ориентированное программирование»

Лучше всего, если задание выберет себе сам студент исходя из своих интересов. Тем, кто не может выбрать самостоятельно, можно рекомендовать темы:

  1. Текстовый редактор (загрузка текста из файла, запись текста в файл, окно редактирования).
  2. Графический редактор.
  3. Калькулятор (N – разрядные целые числа, использование "длинной арифметики").
  4. Осциллограф.
  5. Игра в шахматы (два игрока - люди).
  6. Игра в шашки (два игрока - люди).
  7. Игра в карты «дурак» (переводной - 2 игрока).
  8. Электрическая схема постоянного тока (элементы: батарейка, провод, выключатель, лампочка) - моделирование.
  9. Электрическая схема переменного тока (элементы: источник напряжения, резистор, конденсатор, катушка индуктивности).
  10. Лифт - наглядное моделирование.
  11. Офисная АТС - наглядное моделирование.
  12. Канал передачи данных (несколько источников/получателей, один канал).
  13. Управление светофорами на перекрестке (режимы – с пульта и автоматический).
  14. Датчик случайных чисел (распределения – равномерное, нормальное, экспоненциальное, Бернулли) c методами определения математического ожидания, дисперсии и среднеквадратичного отклонения.