ООП - парадигма программирования, в которой основными концепциями являются понятия объектов и классов.
Объект - экземпляр класса.
Состояние объекта - значение всех его полей.
Базовые принципы ООП:
Инкапсуляция
Инкапсуляция имеет две трактовки:
- это механизм яп, позволяющий связывать (логически объединять) данные и методы в единый объект.
- это сокрытие структуры объекта от внешних воздействий: все изменения состояния объекта происходят только при обращении к методам самого объекта
Наследование
Класс может наследовать данные и методы некоторого другого существующего класса, и использовать их как свои.
Полиморфизм
Это свойство, которое позволяет одно и то же имя (например метода), использовать для решения нескольких внешне схожих, но технически разных задач. К примеру посчитать площадь у окружности, квадрата и треугольника, однако все три метода назвать 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 {};
Если в базовом классе виртуальная функция определена как 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 - беда) , а вместо этого использовать интерфейсы.