seminar2
This commit is contained in:
parent
46d1c64684
commit
ab6732eded
98 changed files with 10319 additions and 0 deletions
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
Это программа на языке C, для компиляции:
|
||||
gcc 00point.c
|
||||
./a.out
|
||||
|
||||
Опишем структуру точки в двумерном пространстве на языке C
|
||||
|
||||
Точка задаётся двумя координатами x и y
|
||||
Так как эта структура имеет очень маленький размер (всего 8 байт), то в функции
|
||||
её можно передавать по значению, а не по константному указателю.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
struct point
|
||||
{
|
||||
float x, y;
|
||||
};
|
||||
typedef struct point Point;
|
||||
|
||||
|
||||
void point_print(Point a)
|
||||
{
|
||||
printf("(%.2f, %.2f)\n", a.x, a.y);
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
Point a = {7.2, 3.1};
|
||||
Point b = {-4.6, 2.4};
|
||||
|
||||
point_print(a);
|
||||
point_print(b);
|
||||
}
|
||||
|
||||
/*
|
||||
Задачи:
|
||||
|
||||
1) Напишите функцию point_add,
|
||||
которая будет принимать две точки и возвращать их сумму
|
||||
|
||||
2) Напишите функцию point_norm,
|
||||
которая будет принимать точку и возвращать расстояние до этой точки от начала координат
|
||||
Будем называть расстояние от точки до начала координат нормой точки
|
||||
Для вычисления корня числа можно использовать функцию sqrt из math.h
|
||||
|
||||
3) Напишите функцию point_mult,
|
||||
которая будет принимать на вход точку и число k типа float и возвращать точку, координаты которой
|
||||
равны координатам изначальной точки, умноженные на число k
|
||||
|
||||
4) Напишите функцию point_normalize,
|
||||
которая будет принимать точку по указателю и делить координаты точки на норму точки
|
||||
Эта функция не должна ничего возвращать
|
||||
*/
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
Это программа на языке C, для компиляции:
|
||||
gcc 00point_solution.c
|
||||
./a.out
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
|
||||
struct point
|
||||
{
|
||||
float x, y;
|
||||
};
|
||||
typedef struct point Point;
|
||||
|
||||
|
||||
void point_print(Point a)
|
||||
{
|
||||
printf("(%.2f, %.2f)\n", a.x, a.y);
|
||||
}
|
||||
|
||||
Point point_add(Point a, Point b)
|
||||
{
|
||||
Point result = {a.x + b.x, a.y + b.y};
|
||||
return result;
|
||||
}
|
||||
|
||||
float point_norm(Point a)
|
||||
{
|
||||
return sqrtf(a.x * a.x + a.y * a.y);
|
||||
}
|
||||
|
||||
Point point_mult(Point a, float k)
|
||||
{
|
||||
Point result = {k * a.x, k * a.y};
|
||||
return result;
|
||||
}
|
||||
|
||||
void point_normalize(Point* pa)
|
||||
{
|
||||
float norm = point_norm(*pa);
|
||||
pa->x /= norm;
|
||||
pa->y /= norm;
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
Point a = {7.2, 3.1};
|
||||
Point b = {-4.6, 2.4};
|
||||
|
||||
point_print(a);
|
||||
point_print(b);
|
||||
|
||||
Point c = point_add(a, b);
|
||||
point_print(c);
|
||||
|
||||
point_print(point_mult(c, 1.5f));
|
||||
|
||||
point_normalize(&c);
|
||||
point_print(c);
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
Это программа на языке C++, для компиляции:
|
||||
g++ 01point.cpp
|
||||
|
||||
Та же самая точка, но на языке C++
|
||||
|
||||
В этом файле была видоизменена программа из предыдущего файла.
|
||||
Были использованы перегруженные операторы для более удобного сложения и умножения точек.
|
||||
Также была использована ссылка вместо указателя в функции pointNormalize.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <cmath>
|
||||
using std::cout, std::endl;
|
||||
|
||||
|
||||
struct Point
|
||||
{
|
||||
float x, y;
|
||||
};
|
||||
|
||||
void pointPrint(Point a)
|
||||
{
|
||||
cout << std::setprecision(2) << "(" << a.x << ", " << a.y << ")" << endl;
|
||||
}
|
||||
|
||||
Point operator+(Point a, Point b)
|
||||
{
|
||||
Point result = {a.x + b.x, a.y + b.y};
|
||||
return result;
|
||||
}
|
||||
|
||||
float pointNorm(Point a)
|
||||
{
|
||||
return std::sqrt(a.x * a.x + a.y * a.y);
|
||||
}
|
||||
|
||||
Point operator*(Point a, float k)
|
||||
{
|
||||
Point result = {k * a.x, k * a.y};
|
||||
return result;
|
||||
}
|
||||
|
||||
void pointNormalize(Point& a)
|
||||
{
|
||||
float norm = pointNorm(a);
|
||||
a.x /= norm;
|
||||
a.y /= norm;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
Point a = {7.2, 3.1};
|
||||
Point b = {-4.6, 2.4};
|
||||
|
||||
pointPrint(a);
|
||||
pointPrint(b);
|
||||
|
||||
Point c = a + b;
|
||||
pointPrint(c);
|
||||
|
||||
pointPrint(c * 1.5f);
|
||||
|
||||
pointNormalize(c);
|
||||
pointPrint(c);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Задача:
|
||||
|
||||
1) Инкапсулируйте функции operator+, pointNorm, operator* и pointNormalize
|
||||
Их нужно сделать методами, то есть положить внутрь структуры Point
|
||||
Не забудьте сделать соответствующие методы константными
|
||||
|
||||
2) Можно сделать то же самое с функцией printPoint, а можно поступить по-другому
|
||||
и перегрузить оператор << для типов std::ostream и Point
|
||||
*/
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
Обратите внимание на следующие моменты в этом решении:
|
||||
|
||||
1) При переносе функций внутрь класса у них стало на 1 аргумент меньше
|
||||
Просто потому что все методы неявно принимают вызывающий их объект
|
||||
|
||||
Например, если a это точка, то при вызове:
|
||||
a.norm()
|
||||
|
||||
метод norm 'знает', что его вызвала имено точка a и может доступаться до её полей x и y
|
||||
|
||||
|
||||
2) Перегруженные операторы тоже могут быть методами
|
||||
При этом оператор преобразуется следующим образом:
|
||||
|
||||
a @ b -> a.operator@(b)
|
||||
где на месте @ может быть любой бинарный оператор
|
||||
|
||||
Например, сложение преобразуется так:
|
||||
a + b -> a.operator+(b)
|
||||
|
||||
Обратите внимание, что перегруженный оператор может стать методом только первого аргумента
|
||||
|
||||
|
||||
3) Перегрузка оператора << для типов std::ostream и Point
|
||||
Для более удобного вывода на экран можно перегрузить этот оператор
|
||||
|
||||
Когда компилятор встретит выражение cout << a где cout имеет тип std::ostream, а имеет тип Point
|
||||
то он вызовет эту функцию.
|
||||
Эта функция должна вызывать ссылку на cout так как результатом cout << a тоже должен быть cout
|
||||
чтобы мы могли выводить цепочкой, например так: cout << a << b << endl
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <cmath>
|
||||
using std::cout, std::endl;
|
||||
|
||||
|
||||
struct Point
|
||||
{
|
||||
float x, y;
|
||||
|
||||
Point operator+(Point b) const
|
||||
{
|
||||
Point result = {x + b.x, y + b.y};
|
||||
return result;
|
||||
}
|
||||
|
||||
float norm() const
|
||||
{
|
||||
return std::sqrt(x * x + y * y);
|
||||
}
|
||||
|
||||
Point operator*(float k) const
|
||||
{
|
||||
Point result = {k * x, k * y};
|
||||
return result;
|
||||
}
|
||||
|
||||
void normalize()
|
||||
{
|
||||
float normv = norm();
|
||||
x /= normv;
|
||||
y /= normv;
|
||||
}
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, Point a)
|
||||
{
|
||||
out << std::setprecision(2) << "(" << a.x << ", " << a.y << ")";
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
Point a = {7.2, 3.1};
|
||||
Point b = {-4.6, 2.4};
|
||||
|
||||
cout << a << endl;
|
||||
cout << b << endl;
|
||||
|
||||
Point c = a + b;
|
||||
cout << c << endl;
|
||||
|
||||
cout << c * 1.5f << endl;
|
||||
|
||||
c.normalize();
|
||||
cout << c << endl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <cmath>
|
||||
using std::cout, std::endl;
|
||||
|
||||
|
||||
struct Point
|
||||
{
|
||||
float x, y;
|
||||
|
||||
Point operator*(float k) const
|
||||
{
|
||||
Point result = {k * x, k * y};
|
||||
return result;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, Point a)
|
||||
{
|
||||
out << std::setprecision(2) << "(" << a.x << ", " << a.y << ")";
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
Point a = {2.1, 1.5};
|
||||
|
||||
cout << a * 2 << endl;
|
||||
cout << 2 * a << endl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Задачи:
|
||||
|
||||
1) В этой программе выражение a * 2 вычисляется правильно, но
|
||||
выражение 2 * a даёт ошибку.
|
||||
|
||||
Из-за чего это происходит? Исправьте ситуацию так, чтобы выражение 2 * a также вычислялось.
|
||||
|
||||
|
||||
2) Можно ли сделать перегруженный оператор << методом класса Point?
|
||||
*/
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
Решения:
|
||||
|
||||
1) Выражение a * 2 вычисляется так как есть перегруженный оператор a.operator*(2)
|
||||
Выражение 2 * a даёт ошибку так как не было перегруженного оператора operator*(2, a)
|
||||
|
||||
Но его можно просто написать, как это сделано ниже.
|
||||
|
||||
Сделать этот оператор методом мы не можем, так как перегруженный оператор может быть методом
|
||||
только первого аргумента, первый аргумент в данном случае это число целочисленного типа float.
|
||||
Добавить метод в тип float мы не можем, так как float это вообще не класс.
|
||||
|
||||
Замечание: Литерал 2 на самом деле имеет тип int, но int может конвертироваться во float если нужно
|
||||
|
||||
|
||||
2) Можно ли сделать перегруженный оператор << методом класса Point?
|
||||
|
||||
Нет, нельзя. Перегруженный оператор может быть методом только первого аргумента.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <cmath>
|
||||
using std::cout, std::endl;
|
||||
|
||||
|
||||
|
||||
struct Point
|
||||
{
|
||||
float x, y;
|
||||
|
||||
Point operator*(float k) const
|
||||
{
|
||||
Point result = {k * x, k * y};
|
||||
return result;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Point operator*(float k, Point a)
|
||||
{
|
||||
return a * k;
|
||||
}
|
||||
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, Point a)
|
||||
{
|
||||
out << std::setprecision(2) << "(" << a.x << ", " << a.y << ")";
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
Point a = {2.1, 1.5};
|
||||
|
||||
cout << a * 2 << endl;
|
||||
cout << 2 * a << endl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
До этого момента поля x и y класса Point были публичными
|
||||
|
||||
Обычно, мы не хотим делать поля публичными, так как мы не хотим, чтобы поля могли бы быть заданы некоректным значением извне класса.
|
||||
Однако, в случае класса Point некорректных значений для x и y просто не существует - любое вещественное число будет
|
||||
корректным для значения координаты точки.
|
||||
Поэтому нет ничего плохого, чтобы сделать x и y публичными для класса Point
|
||||
|
||||
|
||||
Но давайте сделаем класс с немного более сложным поведением.
|
||||
Точка RestrictedPoint - это будет точка, которая может находится только в квадрате [0,1]x[0,1]
|
||||
То есть поле x может принимать значения только от 0 до 1 и поле y может принимать значения только от 0 до 1.
|
||||
|
||||
Сделаем поля x и y приватными (и назовём их mx и my)
|
||||
Теперь до них можно будет доступиться только в методах класса RestrictedPoint и в друзьях.
|
||||
|
||||
Чтобы можно было работать с этими полями вне класса напишем методы
|
||||
getx, gety, setx, sety
|
||||
Такие методы для получения полей и записи в поля класса называются геттерами и сеттерами
|
||||
Функции getx и gety просто возвращают соответствующие координаты
|
||||
Функции setx и sety меняют соответствующие координаты и следят, чтобы они находились в диапазоне от 0 до 1
|
||||
|
||||
|
||||
Нам понадобится стандартная функция std::clamp из <algorithm>, которая принимает на вход три числа и
|
||||
если первое число находится в промежутке между вторым и третьим, то clamp возвращает первое число
|
||||
если первое число меньше, чем второе, то clamp возвращает второе число
|
||||
если первое число больше, чем третье, то clamp возвращает третье число
|
||||
Грубо говоря clamp ограничивает число в заданых пределах
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
using std::cout, std::endl;
|
||||
|
||||
|
||||
|
||||
class RestrictedPoint
|
||||
{
|
||||
private:
|
||||
float mx, my;
|
||||
|
||||
public:
|
||||
|
||||
RestrictedPoint(float x, float y)
|
||||
{
|
||||
mx = std::clamp(x, 0.0f, 1.0f);
|
||||
my = std::clamp(y, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
RestrictedPoint()
|
||||
{
|
||||
mx = 0;
|
||||
my = 0;
|
||||
}
|
||||
|
||||
float getx() const
|
||||
{
|
||||
return mx;
|
||||
}
|
||||
|
||||
float gety() const
|
||||
{
|
||||
return my;
|
||||
}
|
||||
|
||||
void setx(float x)
|
||||
{
|
||||
mx = std::clamp(x, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
void sety(float y)
|
||||
{
|
||||
my = std::clamp(y, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
float norm() const
|
||||
{
|
||||
return std::sqrt(mx*mx + my*my);
|
||||
}
|
||||
|
||||
RestrictedPoint operator+(const RestrictedPoint& right) const
|
||||
{
|
||||
RestrictedPoint result;
|
||||
result.mx = std::clamp(mx + right.mx, 0.0f, 1.0f);
|
||||
result.my = std::clamp(my + right.my, 0.0f, 1.0f);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const RestrictedPoint& a)
|
||||
{
|
||||
out << "(" << a.getx() << ", " << a.gety() << ")";
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
RestrictedPoint a = RestrictedPoint(0.5, 1.2);
|
||||
cout << a << endl;
|
||||
|
||||
a.setx(2);
|
||||
cout << a << endl;
|
||||
|
||||
a.sety(-5);
|
||||
cout << a << endl;
|
||||
|
||||
|
||||
RestrictedPoint b = RestrictedPoint(0.4, 0.2);
|
||||
RestrictedPoint c = RestrictedPoint(0.8, 0.4);
|
||||
cout << c + b << endl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Задача:
|
||||
|
||||
1) Добавьте к классу RestrictedPoint оператор умножения на число типа float
|
||||
|
||||
*/
|
||||
Reference in a new issue