Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Lections_rus.doc
Скачиваний:
31
Добавлен:
06.02.2016
Размер:
1.41 Mб
Скачать

10.2. Отношения между классами

Между классами возможны следующие виды отношений:

  • Ассоциация

  • Агрегирование (с владением и без владения)

  • Наследование

  • Использование

10.2.3. Ассоциация

Смысловая связь.

Пример: сделки/товары, группы/студенты и т.д.

Типы: один к одному (сделка/проводка), один ко многим (группа/студент), многие ко многим (продавец/покупатель).

10.2.4. Агрегирование

Отношение вида “часть”. Когда один объект состоит из других объектов.

Примеры:

  • Агрегирование с владением: части автомобиля.

  • Агрегирование без владения: аэропорт и самолёты.

10.2.5. Наследование

Наследование ­– это такое отношение между классами, когда один класс повторяет  структуру и поведение другого класса (одиночное наследование) или других (множественное наследование) классов.

Класс, поведение и структура которого наследуется называется базовым (родительским) классом, а класс, который наследует – производным или.

В производном классе структура и поведение базового класса дополняются и переопределяются. Производный класс является уточнением базового класса.

«Лакмусовая бумажка» наследования – обратная проверка; так, если В не есть А, то В не стоит производить от А.

 

В С++ наследование используется в двух целях:

  • Наследование интерфейсов

  • Наследование структуры и реализации класса

 

10.3. Библиотека графических объектов (пример)

Наследование должно использоваться только для выражения общности объектов. Когда выполняется правило «является». Нежелательно использовать наследование для компоновки объектов. Наследование есть классификация.

 

Запись наследования в C++:

 

Наследование в С++

class Shape {

  // …

};

 

class Sphere : public Shape {

  // …

};

 

В данном случае указывается, что класс Sphereнаследует классуShape. Наследование означает, что производный класс повторяет структуру базового класса и наследует все его методы.

 

Далее рассматривается пример конкретной иерархии классов.

 

Базовый класс Shape(фигура) содержит координаты фигуры и метод перемещения фигуры в заданную позицию. Также он содержит перегружен

ный конструктор, создающий фигуру в начале координат или в точке, указанной пользователем

 

Базовый класс – фигура

class Shape {   vec2d pos;

public:

  Shape();

  Shape(const vec2& aPos);

 

  void MoveTo(const vec2d& newPos);

};

 

Shape::Shape() : pos(0.0,0.0) {}

 

Shape::Shape(const vec2& aPos) : pos(aPos) {}

 

void Shape::MoveTo(const vec2d& newPos) {

  pos = newPos;

}

 

Класс Circle(окружность) наследует классуShape(фигура). Здесь выполняется правило «окружность является фигурой». Класс привносит новый член данныхradius– атрибут, специфичный для окружности и два метода –Draw(нарисовать фигуру) иErase(стереть фигуру).

Методы DrawиEraseне имеет смысла определять в классеShape, поскольку не известно, как нарисовать или стереть абстрактную фигуру.

Окружность

 

class Circle : public Shape {

  double radius; public:

  Circle(double aRadius);

  Circle(double aRadius, const vec2d& aPos);

 

  void Draw() const;

  void Erase() const;

};

 

Circle::Circle(double aRadius) : radius(aRadius) {}

 

Circle::Circle(double aRadius, const vec2d& aPos) : Shape(aPos), radius(aRadius) {}

 

 

Класс вводит собственный перегруженный конструктор, который позволит создавать объекты типа Circle.

 

При реализации конструктора, создающего окружность с заданным радиусом, инициализируется только член данных radius. Положение фигуры (окружности) в пространстве инициализируется по умолчанию конструктором классаShape. То есть до создания объектаCircleвначале автоматически создаётся унаследованная отShapeчасть и вызывается её конструктор.

 

В случае с конструктором, создающим окружность с заданным радиусом в заданных координатах, конструктор Shapeвызывается явно с передачей ему требуемого положения в пространстве. Из двух форм перегруженного конструктораShapeвызывается второй – размещающий фигуру в пространстве.

 

Пользователь может использовать созданный класс Circleследующим образом:

Использование класса Circle

 

Circle cir1(20.0);

cir1.MoveTo(vec2(200.0,200.0));

cir1.Draw();

cir1.Erase();

 

Circle cir2(20.0,vec2(100.0,100.0));

cir2.Draw();

cir2.Erase();

 

Класс Square(квадрат) также наследует фигуре:

 

Прямоугольник

class Rectangle : public Shape {

  double orientation;

  double high, width; public:

  Rectangle(double aHigh, double aWidth);

  Rectangle(double aHigh, double aWidth, const vec2& pos, double aOrientation=0.0);

 

  void Draw() const;

  void Erase() const;

 

  void Rotate(double newOrientation);

  void SetHigh(double newHigh);

  void SetWidth(double newHigh);

};

 

 

Часто при проектировании допускается типовая ошибка:

 

Квадрат

class Square : public Rectangle {

public:

 Square (double aSide);

 Square (double aSide, const vec2&pos, double aRotation=0.0);

 

  void SetSide(double newSize);

};

 

Кажется, что в данном классе всё хорошо, но из-за нарушения основного принципа при наследовании («объект наследуемого класса является объектом базового класса») создаётся неправильная иерархия. Дело в том, что квадрат только в математическом смысле является прямоугольником. Но он не является таковым с точки зрения обязательств. То есть квадрат не является фигурой, для которой возможно независимое изменение размеров сторон. А поэтому в рассматриваемой иерархии классов он не является прямоугольником.

Ошибка наследования видна сразу: классу Squareизбыточно хранить и высоту и ширину. Но главное, что интерфейс класса, добавив методSetSideнеобходимый для установления длинны стороны квадрата, не может скрыть от пользователя методыSetHighиSetWidth, унаследованные от классаRectangle.

 

Использование класса Square, приводящее к ошибке

Square Square1(10.0);

Square1.SetHigh(20.0);

 

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

В рассматриваемой иерархии классов класс Squareправильнее наследовать классуShape.

 

Рассмотренное семейство классов фигур демонстрирует возможность использования наследования для достижения повторного использования кода. Единожды реализованное понятие фигуры (координаты, перемещение и т.д.) реализуется в производных классах. Каждому из производных классов нет необходимости определять соответствующие члены данных и методы. Всё это реализовано в базовом классе. В случае, если будет необходимо добавить новый функционал (например, управление цветом фигур), он может быть добавлен в базовый класс и производные классы воспользуются готовым решением без переработки собственной функциональности (или с минимальными изменениями).

 

 

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]