Skip to the content.

ООП - парадигма программирования, в которой основными концепциями являются понятия объектов и классов.

Объект - экземпляр класса.

Состояние объекта - значение всех его полей.

Базовые принципы ООП:

Инкапсуляция

Инкапсуляция имеет две трактовки:

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

Класс может наследовать данные и методы некоторого другого существующего класса, и использовать их как свои.

Полиморфизм

Это свойство, которое позволяет одно и то же имя (например метода), использовать для решения нескольких внешне схожих, но технически разных задач. К примеру посчитать площадь у окружности, квадрата и треугольника, однако все три метода назвать square (object[i].square()).

Абстракция

Позволяет работать с объектами не вдаваясь в особенности их реализации (через интерфейс). В этом нам помогает инкапсуляция.

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

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

Наследование определяет отношение IS A, то есть “является”. Например: студент являеться человеком.

Ассоциация

Ассоциация - это отношение, при котором объекты одного типа неким образом связаны с объектами другого типа. Например, объект одного типа содержит или использует объект другого типа. Например, игрок играет в определенной команде:

class Team
{};

class Player
{
public: 
    Team team;
};

Нередко на диаграммах при отношении ассоциации указывается кратность связей. В данном случае связь 1 ко многим. То есть одна команда будет соответствовать многим игрокам.

Агрегация и композиция являются частными случаями ассоциации!

Композиция

Композиция определяет отношение HAS A, то есть отношение “имеет”. Например: класс автомобиля содержит объект класса двигателя:

class Engine
{};

class Car
{
    Engine engine;
};

При этом класс автомобиля полностью управляет жизненным циклом объекта двигателя. При уничтожении объекта автомобиля в области памяти вместе с ним будет уничтожен и объект двигателя. И в этом плане объект автомобиля является главным, а объект двигателя - зависимой.

Агрегация

От композиции следует отличать агрегацию. Она также предполагает отношение HAS A, но реализуется она иначе:

class Engine // abstract
{};
 
class Car
{
public:
    Engine engine;
    Car(Engine eng)
    {
        engine = eng;
    }
};

При агрегации реализуется слабая связь, то есть в данном случае объекты Car и Engine будут равноправны. В конструктор Car передается ссылка на уже имеющийся объект Engine.

Реализация

Реализация предполагает определение интерфейса и его реализации в классах. Например, имеется интерфейс IMovable с методом Move, который реализуется в классе Car:

class IMovable
{
public:
    virtual void Move() = 0;
};

class Car : public IMovable
{
public:
    virtual void Move()
    {
        std::cout << "Машина едет";
    }
};

Принцип подстановки Барбары Лисков - Liskov Substitution Principe (LSP)

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

Этот принцип является важнейшим при принятии решения наследовать или нет.


Модификаторы доступа при наследовании

В терминах ООП, только public наследование является настоящим наследованием.

struct A {};

public наследование - данные наследуются без изменения доступа к ним

struct B1 : public A {};

private наследование - все унаследованные данные станут private

struct B2 : private A {};

protected наследование - все унаследованные данные станут protected

struct B3 : protected A {};

inheritance modifiers

Если в базовом классе виртуальная функция определена как private, то в производном классе её можно переопределить как public.

Если в базовом классе виртуальная функция определена как public, то в производном классе её можно переопределить как private.


Реализация абстрактных методов

На самом деле абстрактный метод можно реализовать и использовать например в дочерних классах как базовую реализацию:

struct NetworkDevice
{
  virtual void send(void *data, size_t size) = 0; // абстрактный метод  
};

void NetworkDevice::send(void *data, size_t size) {// реализация}

struct Router : NetworkDevice
{
  void send(void *data, size_t size) {NetworkDevice::send(data,size);}  
};

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

Интерфейс - абстрактный класс, у которого отсутсвуют поля, а все его методы являются чистыми виртуальными.

Один класс может унаследовать сразу множество классов:

struct Person {};
struct Student : Person {};
struct Worker : Person {};
struct WorkingStudent : Student, Worker {};

Стоит избегать множественное наследование (например WorkingStudent хранит две копии данных Person - беда) , а вместо этого использовать интерфейсы.

Виртуальное наследование - решение прошлой проблемы