C++. Типи відношень між класами: is-a, has-a, uses. Приклади. Спадковість. Основні поняття

C++. Типи відношень між класами: is-a, has-a, uses. Приклади


Зміст


1. Типи відношень між класами. Класифікація

Між класами можливі два типи відношень:

  • 1. Відношення типу is-a (є, являється) при якому один клас є підвидом іншого класу. При такому співвідношенні один клас розширює (деталізує) можливості іншого класу. Розширення можливостей класу здійснюється завдяки використанню спадковості.
  • 2. Відношення, при якому існує взаємозв’язок між двома класами. Тут виділяють два підвиди взаємозв’язку між класами:
    • 2.1. Відношення типу has-a (клас містить інший клас). У цьому випадку в класі оголошується один або декілька екземплярів іншого класу. При даному відношенні можливі два випадки взаємодії. Перший випадок, це коли об’єкт (екземпляр), що оголошений в класі, не є складовою частиною класу (агрегація) і його використання не впливає на функціональну роботу класу. Другий випадок, коли об’єкт, що оголошений в класі, є складовою частиною цього класу (композиція).
    • 2.2. Відношення типу uses (клас “використовує інший клас). У цьому випадку клас містить програмний код іншого вкладеного класу, до якого він має доступ. Більш детально про особливості використання відношення типу uses описується тут.

 

2. Детальний приклад найпростішого типу відношення is-a (спадковість)

Суть відношення типу is-a полягає в тому, що клас є підвидом іншого класу. У даному прикладі базовий клас Circle розширюється класом CircleColor. Клас CircleColor є підвидом класу Circle і додає до нього поле кольору.

У класі Circle реалізовано наступні елементи:

  • внутрішні приховані (private) поля x, y, r;
  • конструктор Circle() з 3 параметрами, які заповнюють значення внутрішніх полів;
  • методи доступу GetXYR(), SetXYR() до полів класу;
  • функція Area() обчислення площі круга.

У похідному класі CircleColor реалізовано поля та методи які доповнюють клас Color:

  • внутрішнє приховане поле класу color;
  • конструктор CircleColor() з 4 параметрами, який звертається до конструктора класу Color;
  • методи GetColor(), SetColor() для доступу до прихованої змінної color.

Демонстраційний текст програми на C++ типу ConsoleApplication наступний:

#include <iostream>
using namespace std;

// C++. Демонстрація відношення is-a
// Клас, який реалізує коло - базовий клас
class Circle
{
  // 1. Внутрішні приховані поля класу
private:
  double x, y; // Координати центру кола
  double r; // радіус кола

public:
  // 2. Конструктор класу
  Circle(double x, double y, double r)
  {
    this->x = x;
    this->y = y;
    if (r > 0) this->r = r;
    else
        this->r = 1.0;
  }

  // 3. Методи доступу - геттери, сеттери
  void GetXYR(double& x, double& y, double& r)
  {
    x = this->x;
    y = this->y;
    r = this->r;
  }

  void SetXYR(double x, double y, double r)
  {
    this->x = x;
    this->y = y;

    // відкорегувати радіус якщо потрібно
    if (r > 0)
      this->r = r;
    else
      this->r = 1.0;
  }

  // 4. Функція Area() - площа кола
  double Area()
  {
    const double Pi = 3.141592;
    return Pi * r * r;
  }
};

// Клас, який розширює можливості класу Circle - додає колір кола
class CircleColor : public Circle
{
  // 1. Внутрішні змінні класу
private:
  unsigned int color = 0; // колір кола

public:
  // 2. Конструктор
  CircleColor(double x, double y, double r, unsigned int color) :Circle(x, y, r)
  {
    this->color = color;
  }

  // 3. Методи доступу
  unsigned int GetColor()
  {
    return color;
  }

  void SetColor(unsigned int color)
  {
    this->color = color;
  }
};

void main()
{
  // Демонстрація відношення між класами is-a
  // 1. Клас Circle
  // 1.1. Створити екземпляр класу Circle
  Circle cr(1, 3, 2);

  // 1.2. Викликати метод Area() класу Circle
  double area = cr.Area();
  cout << "The instance of class Color:" << endl;
  cout << "area = " << area << endl;

  // 1.3. Взяти значення полів екземпляру cr
  double x, y, r;
  cr.GetXYR(x, y, r);

  // 1.4. Вивести значення полів на екран
  cout << "x = " << x << ", y = " << y << ", radius = " << r << endl;

  // 2. Екземпляр класу CircleColor
  // 2.1. Створити екземпляр класу CircleColor
  CircleColor crCol(2, 4, 6, 1);

  // 2.2. Отримати значення полів екземпляру crCol
  // 2.2.1. Взяти значення кольору     з методу класу CircleColor
  unsigned int col = crCol.GetColor();

  // 2.2.2. Взяти значення x, y, r з методу базового класу Color
  crCol.GetXYR(x, y, r);

  // 2.3. Вивести отримані значення на екран
  cout << "The instance of class ColorCircle: " << endl;
  cout << "color = " << col << endl;
  cout << "x = " << x << endl;
  cout << "y = " << y << endl;
  cout << "r = " << r << endl;

  // 2.4. Викликати метод Area() базового класу Circle
  cout << "area = " << crCol.Area() << endl;
}

Результат виконання програми

The instance of class Color:
area = 12.5664
x = 1, y = 3,   radius = 2
The instance of class ColorCircle:
color = 1
x = 2
y = 4
r = 6
area = 113.097

 

3. Приклади типу відношення has-a між класами

При відношенні has-a клас містить один або декілька об’єктів (екземплярів) іншого класу. Існує два види відношення has-a:

  • агрегація. Це випадок, коли один або декілька вкладених об’єктів не є частиною класу, тобто клас може містити будь-яку кількість таких об’єктів (навіть 0). Для кращого вивчення агрегації дивіться наведені нижче приклади;
  • композиція. У цьому випадку один або декілька вкладених об’єктів є частиною класу, тобто без цих об’єктів неможливе логічне існування самого класу.


 

3.1. Приклади агрегації для типу відношення has-a

У випадку агрегації клас містить множини (один або декілька) об’єктів інших класів, які не є складовою частиною цього класу (не містять код, що доповнює роботу самого класу).

3.1.1. Скорочений приклад. Клас Figures містить екземпляри класів Triangle (трикутник) та Circle (коло)

У даному скороченому коді демонструється агрегація на прикладі класів Triangle, Circle, Figures. Клас Figures може містити різну кількість різних фігур (навіть 0). При можливості до класу Figures може бути додано масиви інших фігур, наприклад, Rectangle (прямокутник). У будь-якому випадку клас Figures буде повноцінним функціонально, отже, це є агрегація.

// Клас трикутник
class Triangle
{
  // Методи та поля класу Triangle
  // ...
};

// Клас, що реалізує коло
class Circle
{
  // Методи та поля класу Circle
  // ...
};

// Клас, що реалізує різні геометричні фігури.
// Використовується тип відношення - агрегація.
class Figures
{
  Triangle tr[10]; // масив трикутників
  unsigned int n_tr; // к-сть трикутників у масиві tr
  Circle cr[10]; // масив кіл
  unsigned int n_cr; // к-сть кіл у масиві cr

  // Інші поля та методи класу
  // ...
};

 

3.1.2. Детальний приклад. Клас BusStation (Автостанція) містить масиви екземплярів класів Bus (автобус) та Car (автомобіль)

У прикладі клас BusStation (Автостанція) містить масиви екземплярів класів Bus (Автобус), Car (Автомобіль). Кількість елементів у масивах класів Bus та Car може змінюватись. Навіть, якщо на автостанції в даний момент часу не буде жодного автобуса чи автомобіля, автостанція буде функціонувати. Це є агрегація.

#include <iostream>
using namespace std;

// C++. Демонстрація відношення has-a
// Максимально-можлива к-сть транспортних засобів на автостанції
const int MAX_VEHICLES = 10;

// Клас Автомобіль
class Car
{
  // 1. Приховані внутрішні поля класу
private:
  string model; // марка автомобіля

public:
  // 2. Конструктори класу
  // 2.1. Головний конструктор класу з 1 параметром
  Car(string _model) :model(_model)
  {
  }

  // 2.2. Конструктор без параметрів,
  //     здійснює виклик головного конструктора
  Car() : Car("")
  {
  }

  // 3. Методи доступу до полів класу
  string GetModel() { return model; }
  void SetModel(string _model)
  {
    model = _model;
  }

  // 4. Метод, що відображає інформацію про автомобіль
  void Print()
  {
    cout << "model = " << model << endl;
  }
};

// Клас Автобус
class Bus
{
  // 1. Приховані внутрішні поля
private:
  string model; // Марка автобуса
  unsigned int seats; // кількість посадкових місць

public:
  // 2. Конструктори
  // 2.1. Конструктор з 2 параметрами - головний конструктор
  Bus(string _model, int _seats) : model(model), seats(_seats)
  {
  }

  // 2.2. Конструктор без параметрів,
  //       здійснює виклик головного конструктора
  Bus() : Bus("", 0) {}

  // 3. Методи доступу
  void Get(string& _model, int& _seats)
  {
    _model = model;
    _seats = seats;
  }

  void Set(string _model, int _seats)
  {
    model = _model;
    seats = _seats;
  }

  // 4. Метод, що відображає інформацію про автобус
  void Print()
  {
    cout << "model = " << model << ", seats = " << seats << endl;
  }
};

// Клас Автостанція - містить масиви класів Bus, Car
class BusStation
{
private:
  Bus B[MAX_VEHICLES]; // масив об'єктів класу Bus - відношення has-a
  unsigned int nBus; // поточна к-сть автобусів
  Car C[MAX_VEHICLES]; // масив об'єктів класу Car
  unsigned int nCar; // поточна к-сть автомобілів

public:
  // 1. Конструктор без параметрів
  BusStation()
  {
    nBus = nCar = 0;
  }

  // 2. Методи доступу, що обробляють масив автобусів B[]
  // 2.1. Отримати інформацію про автобус за його номером
  Bus GetBus(unsigned int number)
  {
    // перевірка, чи коректний номер
    if (number < nBus)
      return B[number];
    else
    {
      cout << "Error. Incorrect number of a bus." << endl;
      return Bus("", 0);
    }
  }

  // 2.2. Додати новий автобус
  void AddBus(string model, unsigned int seats)
  {
    // перевірка, чи можна ще додавати транспортний засіб
    if ((nCar + nBus) < MAX_VEHICLES)
    {
      nBus++; // збільшити к-сть автобусів на 1
      B[nBus - 1].Set(model, seats); // викликати метод класу Bus
      cout << "A new bus is added!" << endl;
    }
    else
    {
      cout << "Cannot add a new bus. Sorry" << endl;
      return;
    }
  }

  // 2.3. Видалити автобус з автостанції за його номером
  void DelBus(unsigned int number)
  {
    if (number < nBus)
    {
      // цикл зсуву
      for (int i = number; i < nBus - 1; i++)
        B[i] = B[i + 1];
      nBus--;
    }
  }

  // 3. Методи доступу, що обробляють масив автомобілів C[]
  // 3.1. Отримати автомобіль за його порядковим номером
  Car GetCar(unsigned int number)
  {
    if (number < nCar)
      return C[number];
    else
    {
      cout << "Error. Incorrect number of a car." << endl;
      return Car("");
    }
  }

  // 3.2. Додати новий автомобіль до автостоянки
  void AddCar(string model)
  {
    // Перевірка, чи є місце на автостоянці
    if ((nCar + nBus) < MAX_VEHICLES)
    {
      nCar++; // збільшити к-сть автомобілів
      C[nCar - 1].SetModel(model); // додати дані автомобіля
      cout << "A new car is added!" << endl;
    }
    else
    {
      cout << "Cannot add a new car. Sorry" << endl;
      return;
    }
  }

  // 3.3. Видалити автомобіль з автостоянки за заданим номером
  void DelCar(unsigned int number)
  {
    if (number < nCar)
    {
      // Цикл поелементного зсуву з позиції number
      for (int i = number; i < nCar - 1; i++)
        C[i] = C[i + 1];
      nCar--;
    }
  }

  // 4. Метод, що виводить інформацію про розміщені
  //   на даний момент транспортні засоби на автостоянці
  void Print()
  {
    cout << "Info about bus station:" << endl;
    cout << "nBus = " << nBus << endl;
    if (nBus > 0)
    {
      cout << "Info about buses:" << endl;
      for (int i = 0; i < nBus; i++)
      {
        B[i].Print();
      }
    }
    cout << "nCar = " << nCar << endl;

    if (nCar > 0)
    {
      cout << "Info about cars:" << endl;
      for (int i = 0; i < nCar; i++)
      {
        C[i].Print();
      }
    }
  }
};

void main()
{
  // Демонстрація використання класу BusStation
  // 1. Оголосити екземпляр класу Автостоянка
  BusStation bs;

  // 2. Додати деякі транспортні засоби
  bs.AddBus("Mersedes Sprinter", 28);
  bs.AddBus("Renault Trafic", 29);
  bs.AddCar("Bentley");
  bs.AddCar("Renault Sandero");

  // 3. Вивести дані про транспорні засоби на екран
  bs.Print();
}

Результат роботи програми:

A new bus is added!
A new bus is added!
A new car is added!
A new car is added!
Info about bus station:
nBus = 2
Info about buses:
model = Mersedes Sprinter, seats = 28
model = Renault Trafic, seats = 29
nCar = 2
Info about cars:
model = Bentley
model = Renault Sandero

 

3.2. Приклади композиції для типу відношення has-a
3.2.1. Спрощений приклад. Клас Car (автомобіль), який містить екземпляри класів Vehicle (Двигун) та Wheel (Колесо)

У прикладі продемонстровано композицію для класів Vehicle, Wheel, Car. В автомобіль (Car) входять двигун (клас Vehicle) та колесо (Wheel), які є його складовою частиною – це є композиція (об’єднання).

// Клас Двигун
class Vehicle
{
  // Поля та методи класу
  // ...
};

// Клас Колесо
class Wheel
{
  // Поля та методи класу
  // ...
};

// Клас Автомобіль - містить обов'язкові елементи,
// які є складовими Автомобіля
class Car
{
  // Екземпляри обов'язкових класів,
  // що є частиною даного класу - це є композиція
private:
  Vehicle veh; // автомобіль містить двигун (обов'язково)
  Wheel whl[4]; // колеса - 4 штуки (обов'язково)

  // Поля та методи класу
  // ...
};

 

3.2.2. Спрощений приклад. Клас Bike (Велосипед) містить екземпляри класів Wheel (Колесо) та Saddle (сідло)

Ще один приклад композиції. Клас Bike (Велосипед) містить екземпляри класів Wheel (Колесо) та Saddle (сідло), які є його складовою частиною. Велосипед не може повноцінно функціонувати без коліс чи сідла, тому це є композиція.

// Клас Колесо
class Wheel
{
  // Поля та методи класу
  // ...
};

// Клас Велосипед, містить колеса, сідло
// Клас сідло
class Saddle
{
  // Поля та методи класу
  // ...
};

// Клас Велосипед, містить об'єкти класів,
// які є частиною Велосипеду (сідло, колеса) - це є композиція
class Bike
{
  Saddle sd; // одне сідло - частина, яка доповнює велосипед
  Wheel whl[2]; // два колеса - частина велосипеду

  // Поля та методи класу Bike
  // ...
};

 


Зв’язані теми