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++ следующий:

#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 bus number." << 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)
    {
      // цикл смещения элементов массива B
      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
  // ...
};

 


Связанные темы