ООП c++

Ответить
ya
^-^
Сообщения: 2336
Зарегистрирован: 16 дек 2021, 19:56

ООП c++

Сообщение ya »

https://www.youtube.com/playlist?list=PLQOaTSbfxUtBm7DxblJZShqBQnBAVzlXX

Шаблоны классов с++ примеры. Обобщенные классы.
https://www.youtube.com/watch?v=SDRMUgJwro0
ya
^-^
Сообщения: 2336
Зарегистрирован: 16 дек 2021, 19:56

Базовые понятия ООП. Стэк данных: инкапсуляция

Сообщение ya »

Базовые понятия ООП
https://www.youtube.com/watch?v=0uCwMnZVb9o
Исходники
https://github.com/TheEntityCircle/oop-2nd-term/tree/master/2022/lection01

Стэк данных: инкапсуляция - упаковка кода на внешний интерфейс и внутреннюю реализацию

Создание типа stack

Код: Выделить всё

class stack
{
	public:
		stack(int size); 		// Конструктор
		~stack(); 			// Деструктор			Очищает память
		void push(int a);		// <-|			Добавляет элемент в стэк
		int pop();			// <-|- Методы	
		void clear();			// <-|			Сбрасывает память в 0
	private:
		int size;			// Размер стэка
		int* data;			// Указатель на данные
		int top;			// Верхний элемент
};
Реализация: конструктор, инициализирует поля класса при его создании

Код: Выделить всё

stack::stack(int stack_size)
{
	this->top = 0;
	this->size = stack_size;
	this->data = new int[stack_size];
}
Деструктор

Код: Выделить всё

stack::~stack()
{
	delete[] this->data;
}
Добавляет новый элемент в стек

Код: Выделить всё

void stack::push(int a)
{
	if(this->top == this->size)
	{
		cout << "Стэк заполнен" << endl;
		return;
	}
	this->data[this->top] = a;
	this->top++;
}
Создание объекта main

Код: Выделить всё

{
	stack s1(10); 		// Инициализация стэка на 10 элементов
	s1.push(1);		// Добавление элемента в стэк
	si.push(2);
	cout << s1.pop << endl;
	cout << s1.pop << endl;
}
valgrind ./a.out -g - позволяет смотреть ошибки и утечки памяти
ya
^-^
Сообщения: 2336
Зарегистрирован: 16 дек 2021, 19:56

Базовые понятия ООП. Наследование

Сообщение ya »

Базовые понятия ООП. Наследование
https://www.youtube.com/watch?v=oDcZY31YZns

Исходники
https://github.com/TheEntityCircle/oop-2nd-term/tree/master/2022/lection02

Базовый класс, реализацию класса можно писать как снаружи (см. выше пост), так и внутри (как в данном примере)

Код: Выделить всё

class Animal
{
	protected: // Закрывает доступ всем снаружи и открывает доступ только своим потомкам (промежуточный вариант между private и public)
		bool brain;
	public:
		Animal() {	this->brain = true; }
		void say() { cout << " (silence) " << endl; }
		bool hasBrain() { return brain; }
};
Дочерний класс базового класса

Код: Выделить всё

class Turtle: public Animal
{
	public:
		Turtle() { }
};
Дочерний класс базового класса

Код: Выделить всё

class Crow: public Animal
{
	public:
		Crow() { }
		void say() 	// Перекрыли метод предка и добавили своё поведение
		{ 
			cout << "Moo" << endl; 
		}
};
Дочерний класс базового класса

Код: Выделить всё

class Dog: public Animal
{
	public:
		Dog() { }
		void say() 	// Перекрыли метод предка и добавили своё поведение
		{ 
			cout << "Woof" << endl; 
		}
};
Дочерний класс наследуется от дочернего класса базового класса

Код: Выделить всё

class CrazyDog: public Dog
{
	public:
		CrazyDog() { this->brain = false; }
		void say() 	// Перекрыли метод предка и добавили своё поведение
		{ 
			cout << "Woof,Woof,Woof" << endl; 
		}
};
Создание объекта

Код: Выделить всё

int main
{
	Turtle t;
	return 0;
}
ya
^-^
Сообщения: 2336
Зарегистрирован: 16 дек 2021, 19:56

Реализация виртуального (абстрактного)

Сообщение ya »

Реализация виртуального (абстрактного) класса подразумевает обязательное наличие метода в неабстрактном классе

Код: Выделить всё

class Figure // Данный класс является абстрактным, потому что содержит в себе метод без реализации
{
	protected: // К этому свойству имеют доступ все потомки этого класса
		float wight;
	public:
		float getWeight() // Позволяет получить доступ на чтение снаружи: геттер
		{
			return weight;
		}
		
		// Синтаксис равно нулю - это сигнал компилятору, что реализацию дальше не ожидать
		virtual float square() = 0; // Виртуальный метод обязан быть реализован в потомке
};
Реализация виртуального метода появляется в потомках

Код: Выделить всё

class Triangle: public Figure
{
	// ...
	float square()
	{
		float p = (a+b+c)/2
		return sqrtf(p*(p-a)*(p-b)*(p-c));
	}
};

class Rectangle: public Figure
{
	//...
	float square() { return a*b; }
};
реализация main

Код: Выделить всё

{
	Figure* figs[15]; // Массив указателей на объекты базового типа Figure
	
	//...
	// По каждому указателю кладёте объекты производных типов 
	// т.е. указатель на тип предка, а реализация тип потомка
	//...
	
	// Для каждого метода будет вызвана своя реализация
	for(int i=0; i<15; i++)
		cout << figs[i]->square() << endl;
}
ya
^-^
Сообщения: 2336
Зарегистрирован: 16 дек 2021, 19:56

Перегрузка при наследовании

Сообщение ya »

Перегрузка при наследовании:
Указатель на объект класса предка, который указывает на объект класса потомка

Код: Выделить всё

Animal* animal = new CrazyDog();
cout << animal->hasBrain() << endl;
animal->say();
Виртуальность включается в том случае, если есть указать на класс предка, а объект класса потомка. Он указывает на объект класса потомка, и вызываемый метод у вас помечен как virtual.

Если есть указать на класс потомка, то указывать на предка он не может.
ya
^-^
Сообщения: 2336
Зарегистрирован: 16 дек 2021, 19:56

Интерфейс

Сообщение ya »

Интерфейс - абстрактный класс, у которого все методы виртуальные
Реализация - класс, унаследованный от интерфейса и реализующий все его виртуальные методы
ya
^-^
Сообщения: 2336
Зарегистрирован: 16 дек 2021, 19:56

Пространства имён

Сообщение ya »

https://github.com/TheEntityCircle/oop-2nd-term/tree/master/2022/lection03

Глобальное пространство имён

Код: Выделить всё

int doSomeWork();
Переменные, функции, классы в пространстве имён A

Код: Выделить всё

namespace A
{
	int doSomeWork();
}
Вызываем функции из разных пространств имён

Код: Выделить всё

{
	doSomeWork();
	A::doSomeWork();
}
Можно построить иерархию из вложенных пространств имён (как матрёшки)

Код: Выделить всё

namespace A
{
	void hello();
	namespace B
	{
		void hello();
	}
}
Вызов функций с явным указанием пути по пространствам имён

Код: Выделить всё

{
	A::hello();
	A::B::hello();
}
Включение содержимого A в глобальное пространство имён

Код: Выделить всё

using namespace A;
теперь вызывать можно так

Код: Выделить всё

{
	hello();
	B::hello();
}
Включение одной функции в глобальное пространство именён

Код: Выделить всё

 using A::B::hello;
ya
^-^
Сообщения: 2336
Зарегистрирован: 16 дек 2021, 19:56

Модификаторы доступа: friend (костыльная конструкция)

Сообщение ya »

Модификаторы доступа: friend (костыльная конструкция и по возможности не использовать)
Класс A позволяет получить доступ к приватным переменным для B
Эта дружба только в одну сторону
Если класс B пометит класс A как friend, тогда эта дружба будет взаимной

Код: Выделить всё

class A
{
	// Теперь B - друг A, и ему всё можно в классе А
	friend class B;
};

class B
{
	public:
		void run(A* a)
		count <<  a->secret << endl;
		a->secret = -1;
}
ya
^-^
Сообщения: 2336
Зарегистрирован: 16 дек 2021, 19:56

Виртуальный деструктор

Сообщение ya »

Виртуальный деструктор

Код: Выделить всё

class IBade
{
	virtual ~IBase() = default;
}

class TestClass: public IBase
{
	public:
		~TestClass();
};

// Теперь деструктор будет корректно вызван
IBase* obj2 = new TestClass(100);
delete obj2;
ya
^-^
Сообщения: 2336
Зарегистрирован: 16 дек 2021, 19:56

override - Позволяет отловить ошибку на этапе компиляции

Сообщение ya »

override (перекрытие) - Позволяет отловить ошибку на этапе компиляции

Код: Выделить всё

class IBase
{
	public:
		virtual void describe(bool foo) = 0;
};

class ClassA : public IBase
{
	public:
	// Перекрытие метода с аргументами и без аргументов - это разные методы, соответственно компилятор выдаст ошибку
	void desctibe() override final;
}

class ClassC final: public ClassA // тут final указывает, что дальше от этого класса наследоваться нельзя
{
	public:
	// тоже ошибку выдаст, потому что final в родительском классе
	void describe() override;
}
override - говорит о взаимодействии с предками о том, что он что-то перекрывает
final - говорит о том, что дальше перекрывать нельзя
ya
^-^
Сообщения: 2336
Зарегистрирован: 16 дек 2021, 19:56

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

Сообщение ya »

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

Код: Выделить всё

class Animal // Интерфейс для движка
{
	virtual void move() = 0;
};

class Drawing //Интерфейс для визуализатора
{
	virtual void show() = 0;
};

// Реализация интерфейсов
class Snake : public Animal , public Drawing
{
	public:
		void move() override;
		void show() override;
};
Обращение к объекту позволяет обращаться по указателю в двух вариантах:

Код: Выделить всё

// Первый вариант позволяет наследовать интерфейс от Animal
Animal* a = new Snake();
a->move();

//Второй вариант позволяет наследовать интерфейс от Drawing
Drawing* d = new Snake();
d->show();
В случае наличия одинаковых методов в предках, обращение к нему должно быть явным

Код: Выделить всё

class TestClass : public A, public B { ... };
...
{
	TestClass t;
	// t.describe(); - неявное обращение к методу
	t.A::describe(); // явное обращение к методу
	t.B::describe(); // явное обращение к методу
}
ya
^-^
Сообщения: 2336
Зарегистрирован: 16 дек 2021, 19:56

Множественное наследование: наличие общего предка

Сообщение ya »

Множественное наследование: наличие общего предка
решается виртуальным наследованием (костыль), и если строится на этом, стоит пересмотреть архитектуру

Код: Выделить всё

class Person { ... };
class Faculty : virtual public Prson { ... };
class Student : virtual public Prson { ... };
class Mentor: public Faculty, public Student { ... };
ya
^-^
Сообщения: 2336
Зарегистрирован: 16 дек 2021, 19:56

Полиморфизм

Сообщение ya »

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

Ещё один пример полиморфизма:

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

Код: Выделить всё

{
	Figure * figs[15];
	for (int i=0; i>15; i++)
		cout << figs[i]->square() << endl;
}
Если метод square() помечен в классе Figure как виртуальный, то при обращении к square() компилятор будет смотреть какого он типа, и соответственно будет вызывать нужную реализацию.

Реальный метод будет вызываться только на этапе запуска.
Ответить