Раньше для хранения данных использовались простые типы данных: числа, строки и т.д. Тем не менее, многие объекты, которые возникают в программировании, нельзя охарактеризовать только одной числовой или строковой величиной. Например, точка на плоскости задается парой действительных чисел (x, y), а данные о человеке можно задавать при помощи нескольких строк (фамилии, имени, отчества) и числового параметра: года рождения.
Поэтому удобным является использование объектов, объединяющих сразу ряд параметров, каждый из которых может фигурировать, как отдельная переменная.
Для этого используются специальные типы данных, которые в языке C
называются структурами, а в языке C++ – классами.
Например, структура Point
, задающая точку на плоскости и
содержащая два действительных числа x
и y
может быть задана следующим образом:
struct Point { double x; double y; };
Переменные x
и y
, входящие в структуру Point
, называются
полями структуры. Определение структуры дается вне всех функций (и, обычно делается перед
объявлением всех функций). Определение структуры обязательно завершается точкой с запятой.
После этого мы можем работать с Point
, как с новым типом данных, содержащим два поля:
x
и y
. Примеры создания переменной и массива переменных типа Point
:
Point P, Arr[10];
Чтобы обратиться к полю какой-либо структуры, используется оператор "точка". Его
левый операнд – идентификатор переменной типа структура, правый операнд –
имя поля. Например, P.x
или Arr[i].y
.
Например, считать координаты точки, сохранить их в переменной P
и
вывести на экран расстояние от начала координат до этой точки можно следующим образом:
cin >> P.x >> P.y; cout << sqrt(P.x * P.x + P.y * P.y);
По сути, величины P.x
и P.y
являются независимыми переменными, скомбинированными
в один объект. С этим объектом можно работать, как с единым целым, например, можно выполнять
присваивания вроде Arr[i]=P
, можно сохранить набор точек в одном массиве и т.д.
Аналогично, можно определить структуру типа Person
для хранения информации о человеке:
struct Person { string FirstName; // Имя string LastName; // Фамилия int BirthYear; // Год рождения };
Теперь можем работать с данными типа Person
:
Person Vasya; Vasya.FirstName = "Василий"; Vasya.LastName = "Пупкин"; Vasya.BirthYear = 1990; cout << Vasya.FirstName << " " << Vasya.LastName << " родился в " << Vasya.BirthYear << " году" << endl;
Полями структур могут быть произвольные типы данных. Можно, например, создать структуру, полями которой будут массивы или другие структуры.
Например, пусть мы хотим определить структуру Circle
для определения окружности. Окружность
задается центром и радиусом. Радиус – это действительное число (поле Radius
типа double
), а центр –
это, конечно же, точка, то есть поле Center
имеет тип Point
. Получили:
struct Circle { Point Center; double Radius; };
Дальше с такими "вложенными" структурами можно работать так:
Circle A; A.Radius = 10; A.Center.x = -3; A.Center.y = 15;
Удобно задавать значения структуры сразу при помощи конструкторов. Конструктор - это специальная функция, которая вызывается при создании объекта. У нее нет возвращаемого значения.
struct Person { string FirstName; // Имя string LastName; // Фамилия int BirthYear; // Год рождения Person(string FirstN, string LastN, int BirthY); };
Сама функция будет определяться так:
Person::Person(string FirstN, string LastN, int BirthY) { FirstName = FirstN; LastName = LastN; BirthYear = BirthY; }Для того, чтобы создавать объекты как прежде
Person p;
,
нужно добавить конструктор по умолчанию, то есть написать еще одну функцию Person::Person()
.
Создайте структуру Point
, определите для нее конструктор, вывод на экран.
У структуры Point
два поля: x
и y
.
Добавьте в структуру Point арифметические операции: прибавление другой точки ( void Point::add(Point &p)) и других.
Программа получает на вход число N, далее координаты N точек. Выведите координаты точки, наиболее удаленной от начала координат.
Для решения этой задачи напишите метод dist
,
который возвращает расстояние от точки до начала координат.
Ввод | Вывод |
---|---|
2 |
2 3 |
Выведите координаты центра масс данного множества точек (учтите, что это —два действительных числа).
Используйте для вычислений метод add()
Ввод | Вывод |
---|---|
2 |
1.5 2.5 |
Выведите диаметр данного множества – максимальное расстояние между двумя данными точками. Для этого перегрузите метожд dist(), сделайте еще один, который принимает другую точку.
Ввод | Вывод |
---|---|
3 |
1.4142135623731 |
Определите для точек оператор сравнения “<”, сравнивающую
точки по значению расстояния от начала координат. Отсортируйте данные
точки в порядке возрастания расстояния от начала координат используя
функцию sort
из файла algorithm
.
Ввод | Вывод |
---|---|
3 |
0 0 |
Среди данных точек найдите три точки, образующие треугольник с наибольшим периметром. Выведите данный периметр.
Для нахождения периметра треугольника напишите отдельную функцию double Perimeter(Point A, Point B, Point C)
.
Ввод | Вывод |
---|---|
4 |
3.41421356237309 |
Среди данных точек найдите три точки, образующие треугольник с наибольшей площадью. Выведите данную площадь.
Для нахождения площади треугольника напишите отдельную функцию double Area(Point A, Point B, Point C)
.
Ввод | Вывод |
---|---|
4 |
0.5 |
Структура Fraction
должна иметь два поля: числитель a
и
знаменатель b
. Оба поля должны быть типа int
.
Для структуры Fraction
определите конструкторы, которые могут принимать
следующие виды параметров:
Fraction
.
int
.
int
.
string
, содержащий два целых числа, записанных
через дробную черту или одно целое число.
В конструкторе принимающем std::string
можно воспользоваться такой конструкцией:
// Пусть a, b - поля Fraction (числитель и знаменатель) // s - параметр типа std::string& int n = sscanf(s.c_str(), "%d/%d", &a, &b); if (n == 1) b = 1;
Этот код мы разберем в подробностях позже.
У каждой дроби существует единственное каноническое представление.
Каноническое представление: это такое представление \(\frac{a}{b}\), что
\(b>0\), \( (a, b) = 1\). Структура должен иметь метод reduce
,
который приводит дробь к каноническому представлению. После каждой операции
с дробями (в том числе и в конструкторе) необходимо вызывать метод reduce
для сокращения дроби.
Необходимо определить вывод дроби в формате a/b
в каноническом представлении, если же дробь является целым
числом, то просто значение этого числа.
Разберемся как работает стандартный ввод и вывод в C++. Оказывается, этот не какой-то специальный синтаксис С++,
а именно перегрузка операций. В языке C++ есть стандартные классы std::istream
и std::ostream
,
экземлярами, то есть объектами которых являются std::cin
и std::cout
.
Запись std::cout << x;
означает применение перегруженной операции <<
к объектам std::cout
и x
.
Для ввода и вывода в привычном виде нужно перегрузить операторы >>
и <<
для классов std::istream
и std::ostream
.
Функция должна быть внешней, потому что первым параметром операции являются std::cin
и std::cout
,
а доступа к коду от классов std::istream
и std::ostream
у нас нет.
Напишем, например, функцию для вывода дроби
std::ostream &operator<<(std::ostream &out, const Fraction &f) { out << f.a << "/" << f.b; return out; }
О слове const мы поговорим позже. В принципе, можно убрать его вместе с амперсандом std::ostream &operator<<(std::ostream &out, Fraction f)
Оператор << (да, <<
- это именно операция,
такая же как +
или *
,
принимает объект вывода (объект типа std::ostream
) и дробь.
В теле функции дробь выводится при помощи переданного ей объекта вывода.
Теперь мы можем выводить дробь так: std::cout << f
.
Функция возвращает переданный ей объект out
для того, чтобы можно было
устраивать цепочки: std::cout << f1 << f2
, эта конструкция выполняется так: (std::cout << f1) << f2
.
Организация ввода при помощи std::cin
делается аналогично.
std::istream &operator>>(std::istream &in, Fraction &f) { // создаем string и читаем в него всю строку со ввода string s; std::getline(in, s); // и формируем дробь из строки s как в конструкторе, показанном выше ... return in; }
Необходимо реализовать следующую функциональность структуры:
reduce
.
Fraction()
, Fraction(int)
, Fraction(int, int)
.
Конструктор должен вызывать метод reduce
.
Методика тестирования следующая. Проект должен содержать два файла:
fraction.h
, содержащий описание и реализацию структуры
и fraction.cpp
, подключающий fraction.h
и содержающий функцию main
. На проверку сдается только
файл fraction.h
, после чего проводится его тестирование на
соответствие спецификации.
Реализовать считывание дроби из потока istream
,
перегрузив оператор считывания >>
. При считывании
дробь имеет один из двух форматов: либо целое число, либо два
целых числа через дробную черту без пробелов, при этом только числитель
может иметь знак “-”.
Определите операторы
<
, <=
, >
, >=
,
==
, !=
. Сравнения необходимо реализовать
для типов int
, double
, Fraction
,
при этом значение типа Fraction
может быть как правым,
так и левым операндом.
Определите оператор сложения +
так, чтобы можно
было складывать:
Fraction
).
int
в любом порядке (результатом является Fraction
).
double
в любом порядке (результатом является double
).
Определите оператор вычитания.
Определите операторы умножения и деления.
Определите операторы +=
, -=
,
*=
, /=
для случая, когда правый
операнд имеет тип Fraction
или int
.
Пример определения оператора:
Fraction & operator+= (Fraction &, Fraction)
Перепишите программу моделирования идеального газа со структурами.
Сделайте структуры Containter
и Particle
, в последней реализуйте методы void draw()
и void move(Containter &containter)
.
Координаты контейнера и центр частицы храните вложенной структурой Point.
При щелчке левой кнопкой в месте каждого касания возникает частица, которая начинает двигаться (на экране появляется несколько шариков, по количеству кликов).
Можно рисовать из линий (незакрашенный).
В момент старта программы одна из лопастей направлена вертикально вверх. Нажатие на правую кнопку увеличивает скорость вращения по часовой стрелке.
Нажатие на левую - уменьшает. Скорость может стать отрицательной.