Файл: Курсовое проектирование.pdf

ВУЗ: Не указан

Категория: Реферат

Дисциплина: Не указана

Добавлен: 04.12.2023

Просмотров: 110

Скачиваний: 5

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
Рекомендации по работе с динамически создаваемыми массивами:
1. Для динамического создания массива необходимо запрашивать память в процессе выполнения программы. В контексте курсовой работы объем запрашива- емой памяти соответствует количеству структур в файле. Рекомендуется запро- граммировать отдельную функцию (например, getSizeOfFileWithStudents()) для вычисления количества структур в файле перед созданием динамического масси- ва. Примеры кода, реализующие данную задачу, приведены в подразделе 6.2.

25 int number_of_students = getCountOfStucturesInFile(FILE_OF_DATA); int
*arr = new int
[
number_of_students
];
// динамически создаваемый массив
2. Для динамически создаваемых массивов необходимо освобождать выде- ленную память в конце программы (в противном случае – утечки памяти): delete
[]arr;
// освобождение памяти, выделенной под динамический массив
3. Способ увеличения размера динамически созданного массива (для добав- ления нового элемента в массив): создаем новый массив, переносим туда содер- жимое старого массива, старый массив удаляем. Пример перевыделения памяти с целью увеличения размера динамически созданного массива приведен в подразде- ле 6.3.
4. Для удаления элемента из динамически созданного массива необходимо сдвинуть все элементы на одну позицию, начиная с номера удаляемого элемента, а затем уменьшить логический размер массива (см. пример кода, реализующего данную задачу, в пояснениях к статически создаваемым массивам).
В качестве альтернативы массивам выступают векторы из стандартной биб- лиотеки шаблонов. Векторы безопаснее и удобнее, чем массив, но в общем случае медленнее с точки зрения производительности.
Рекомендации по работе с векторами:
1. Для полноценной работы с векторами необходимо подключить следующие библиотеки:
#include

#include

#include

2. Для минимизации операций перевыделения памяти для вектора при считы- вании информации из файла рекомендуется после объявления вектора зарезерви- ровать для него память. При этом целесообразно отталкиваться от количества структур в читаемом файле. Наиболее удобно запрограммировать отдельную функцию (например, getCountOfStucturesInFile(
string file_path
)) для вычисления количества структур в файле, примеры кода, реализующие данную задачу, приве- дены в подразделе 6.2. Тогда: vector <
Student
> vector_of_students;
/* резервируем память на getSizeOfFileWithStudents() элементов, но ничем не за- полняем: */
vector_of_students.reserve(getCountOfStucturesInFile(FILE_OF_DATA));


26 3. Передача вектора в функцию осуществляется либо по значению, либо по ссылке (например, если в функции происходят изменения вектора). Размер векто- ра при этом в функцию передавать не нужно (он доступен внутри функции по- средством библиотечного метода size()). Примеры: void showStudents(
vector
<
Student
> vec_of_students
);
// вывод массива на экран void delStudent(
vector
<
Student
> &
vec_of_students
);
// удаление элемента массива
4. Добавление элемента в конец вектора осуществляется посредством биб- лиотечного метода push_back: void readFileStudents(
vector
<
Student
> &
vec_of_students
)
{ ifstream fin(FILE_OF_STUDENTS, ios
::in); if
(!fin.is_open()) cout
<<
"Файл не существует!"
; else
{
Student student_temp; while
(!fin.eof())
{ fin
>>
student_temp.surname
>>
student_temp.name
>>
student_temp.age; vec_of_students
.push_back(student_temp);
}
} fin.close();
}
5. Удаление элемента из вектора, расположенного по индексу index_for_delete, осуществляется посредством библиотечного метода erase (при этом необходимо с помощью итератора begin() выполнить позиционирование в начало вектора, а далее осуществить сдвиг на требуемое число позиций): vec_of_students
.erase(
vec_of_students
.begin()
+
index_for_delete);
6. Для доступа к элементу вектора используется метод at: void writeFileStudents(
vector
<
Student
> vec_of_students
)

27
{ ofstream fout(FILE_OF_STUDENTS, ios
::out); for
(
int i = 0; i < vec_of_students
.size(); i++)
{ fout
<<
vec_of_students
.at(i).surname
<<
" "
<<
vec_of_students
.at(i).name
<<
" "
<<
vec_of_students
.at(i).age; if
(i < vec_of_students
.size() - 1) fout
<<
endl;
} fout.close();
}
7. Сортировку вектора можно выполнить с помощью метода sort из библиоте- ки algorithm, при этом требуется создать дополнительную функцию-компаратор, определяющую, по какому полю и в каком порядке (по возрастанию/по убыва- нию) будет выполнена сортировка: void sortStudentsBySurname(
vector
<
Student
> &
vec_of_students
)
// сортировка
{ sort(
vec_of_students
.begin(), vec_of_students
.end(), mySortBySurname);
} bool mySortBySurname(
Student student_a
,
Student student_b
)
// функция-компаратор
{ return student_a
.surname
<
student_b
.surname;
// по алфавиту: от а до я
}
4.4 Минимизация области видимости переменных
Областью видимости называют фрагмент программы, в котором переменная известна и может быть использована. В C++ переменная может иметь область ви- димости, соответствующую фрагменту кода, заключенному в фигурные скобки, в котором переменная объявлена и используется (локальная переменная) или всей программе (глобальная переменная; объявляется вне блоков описания функций).
Важно: минимизировать область видимости переменной, сделав ее как можно более локальной.


28
Несмотря на то, что глобальные переменные облегчают доступ к ним, так как не нужно беспокоиться о списках параметров и правилах области видимости, удобство доступа к глобальным переменным не может компенсировать связанную с этим опасность. Так программу, в которой каждый метод может вызвать любую переменную в любой момент времени, сложнее понять, чем код, основанный на грамотно организованных методах. Сделав данные глобальными, вы не сможете ограничиться пониманием работы одного метода: вы должны будете понимать ра- боту всех других методов, которые вместе с ним используют те же глобальные данные. Подобные программы сложно читать, сложно отлаживать и сложно изме- нять. В то время как локальная область видимости способствует интеллектуальной управляемости: чем больше информации скрыто, тем меньше нужно удерживать в уме в каждый конкретный момент времени и тем ниже вероятность того, что раз- работчик допустит ошибку, забыв одну из многих деталей, о которых нужно было помнить.
Важно: выбирать локальную область видимости для массивов/векторов.
Глобальные переменные имеют два главных недостатка: функции, обра- щающиеся к глобальным данным, не знают о том, что другие функции также обращаются к этим данным; или же функции знают об этом, но не могут контролировать, что именно другие функции делают с глобальны- ми данными.
4.5 Разделение программы на независимые cpp-файлы и их подключение
с помощью заголовочных h-файлов
Следует выносить код логически независимых модулей в отдельные cpp- файлы и подключать их с помощью заголовочных h-файлов (рисунок 4.4).
Рисунок 4.4 – Способы объявления функций в С++

29
Разделение исходного кода программы на несколько файлов становится не- обходимым по ряду причин:
1. Обеспечение удобства работы с небольшими по объему фрагментами кода, локализованными в отдельных файлах.
2. Разделение программы на логически завершенные модули, которые реша- ют конкретные подзадачи.
3. Разделение программы на физически независимые модули с целью повтор- ного использования этих модулей в других программах.
4. Разделение интерфейса и реализации.
Последний принцип (разделение интерфейса и реализации) требует особого пояснения. Отдельный модуль состоит из двух файлов: заголовочного h-файла
(интерфейс) и cpp-файла реализации. Для удобства h-файл и cpp-файл называют одинаково; имя должно соответствовать смысловой нагрузке данного модуля.
Заголовочный файл подключается в одноименный с ним cpp-файл с реализа- цией программной логики модуля, а также в файл, который будет осуществлять вызов функций данного модуля (например, в файл с запускающей функцией main()). В этом смысле заголовочный файл представляет собой связующее звено между файлом, который задействует логику модуля, и файлом, собственно реали- зующим эту логику. Подклчение заголовочного файла производится посредством директивы препроцессора include, например:
#include "validation.h"
Сам по себе заголовочный файл не является единицей компиляции: на этапе обработки исходного кода препроцессором директива #include "validation.h" заме- няется текстом из файла validation.h.
Заголовочный файл может содержать:

директивы препроцессора;

глобальные константы;

описание типов пользователя;

прототипы функций.
Заголовочный файл не должен содержать описание функций!
Заголовочный файл должен иметь механизм защиты от повторного включе- ния. Защита от повторного включения реализуется директивой препроцессора:
#pragma once
Файл реализации содержит описание функций. Следует отметить, что при не- совпадении сигнатуры функции в прототипе (h-файл) и в определении (cpp-файл) компилятор выдаст ошибку. Также файл реализации может содержать объявления.
Объявления, сделанные в файле реализации, будут лексически локальны для этого


30 файла, т. е. будут действовать только для этой единицы компиляции. При этом в файле реализации не должно быть объявлений, дублирующих объявления в соот- ветствующем заголовочном файле.
В файле реализации должна быть директива включения соответствующего заголовочного файла.
Далее на рисунках 4.5–4.10 приведены примеры выделения в программе неза- висимого модуля, выполняющего проверку вводимых данных на корректность, и его подключения к файлу main.cpp. Данный модуль состоит из заголовочного файла validation.h и файла реализации validation.cpp.
Рисунок 4.5 – Добавление в проект заголовочного файла
Рисунок 4.6 – Создание заголовочного файла validation.h

31
Рисунок 4.7 – Структура проекта с запускающим файлом main.cpp, а также модулем validation, состоящим из заголовочного файла validation.h и файла реализации validation.cpp
Рисунок 4.8 – Пример программной реализации файла main.cpp

32
Рисунок 4.9 – Пример программной реализации файла validation.h
Рисунок 4.10 – Пример программной реализации файла validation.cpp

33
5 Рекомендации по разработке алгоритмов работы программы
Алгоритм – набор инструкций, описывающих порядок действий исполнителя для достижения некоторого результата.В самом упрощенном виде разработку простейшей программы можно представить в виде схемы: анализ задачи → разра- ботка (обдумывание) алгоритма ее решения → программирование (реализация ал- горитма с использованием конкретного алгоритмического языка программирова- ния).Разработать алгоритм решения задачи означает разбить задачу на последова- тельно выполняемые шаги. При этом должны быть четко указаны как содержание каждого шага, так и порядок выполнения шагов.
В блок-схеме алгоритма отдельные шаги изображаются в виде блоков раз- личной формы, соединенных между собой линиями, указывающими направление последовательности. Правила оформления алгоритмов регламентируются ГОСТ
19.701-90 Схемы алгоритмов, программ, данных и систем. Условные обозначения и правила выполнения.
В таблице 5.1 приведены основные символы блок-схем алгоритмов и коммен- тарии по их применению.
Важно: при начертании элементов необходимо придерживаться строгих размеров, определяемых двумя значениями a и b. Значение a выбирается из ряда 15, 20, 25, ... мм, b рассчитывается из соотношения 2a = 3b.
Минимальное количество текста, необходимого для понимания функции символа, следует помещать внутри данного символа. Если объем текста внутри символа превышает его размеры, следует использовать символ комментария.
В таблице 5.2 приведены обозначения соединений между символами блок- схем алгоритмов. Соединения между символами показываются линиями и носят название потоков. Направление потока слева направо и сверху вниз считается стандартным, стрелки в таких потоках не указываются. В случаях если поток име- ет направление, отличное от стандартного (справа налево или снизу вверх), при- менение стрелок обязательно. Стрелки выполняются с развалов в 60°.
Важно: чтобы линии в схемах подходили к центру символа сверху, а
исходили снизу. Исключениями из этого правила являются символы со- единитель, терминатор (с альтернативным сценарием программы), реше- ние и подготовка, допускающие иные направления входных и/или вы- ходных потоков.
Важно: пересечения линий следует избегать, так как это существенно затрудняет восприятие алгоритма.


34
Таблица 5.1 – Символы блок-схем алгоритмов
Название и обозначение на схеме
Функция
Терминатор
Выход во внешнюю среду и вход из внеш- ней среды (начало или конец программы).
На практике имеют смысл следующие описания терминаторов: начало/конец, за- пуск/остановка, ошибка (подразумевает завершение алгоритма с ошибкой), исклю- чение (подразумевает генерацию про- граммного исключения), наимнование действия
(например, имя функ- ции/метода), возврат (подразумевает воз- врат из функции/метода в основную про- грамму каких-либо данных)
Данные
Ввод или вывод данных
Процесс
Обработка данных любого вида (выполне- ние определенной операции или группы операций, приводящее к изменению зна- чения, формы или размещения информа- ции). Например, инициализация перемен- ной, арифметические действия над данны- ми и др.
Предопределенный процесс
Отображение процесса, состоящего из ша- гов программы, которые определены в другом месте. Используется для вызова функции/метода.
Внутри блока записывается имя вызывае- мой функции или метода

35
Продолжение таблицы 5.1
Название и обозначение на схеме
Функция
Решение
Отображение условия на алгоритме. Вход в элемент обозначается линией, входящей в верхнюю вершину элемента, выходы – ли- ниями, выходящими из оставшихся вершин и сопровождающимися соответствующими значениями условий.
Если выходов больше трех (например, в switch-case), то их следует показывать од- ной линией, выходящей из нижней верши- ны элемента, которая затем разветвляется в соответствующее число линий
Границы цикла
Отображение границ цикла.
Обе части символа имеют один и тот же идентификатор.
Условия для инициализации, приращения, завершения помещаются внутри символа в начале или в конце в зависимости от распо- ложения операции, проверяющей условие.
Символ используется для циклов do-while, while, for
Подготовка
Подготовительные операции для счетных циклов (циклов for)
Соединитель
Указание связи между прерванными лини- ями схемы (например, разделение блок- схемы, не помещающейся на листе). Соот- ветствующие символы-соединители долж- ны содержать одно и то же уникальное обо- значение (цифра или буква)

36
Продолжение таблицы 5.1
Название и обозначение на схеме
Функция
Комментарий
Добавление пояснительных записей. Пунк- тирные линии в комментарии связаны с со- ответствующим символом или группой символов (при этом группа выделяется за- мкнутой пунктирной линией). Текст ком- ментария помещается около ограничиваю- щей его по всей высоте скобки.
Комментарий также используется, когда объем текста, помещаемого внутри некоего символа, превышает его размер.
Комментарии используют с терминаторами для описания входных аргументов функ- ции/метода
Таблица 5.2 – Соединения между символами блок-схемы алгоритма
Наименование
Обозначение
на схеме
Функция
Линии потока
Указание направления линии потока:

без стрелки, если линия направлена слева направо или сверху вниз;

со стрелкой в остальных случаях
Излом линии под углом 90°
Изменение направления потока
Пересечение линий потока
Пересечение двух несвязанных потоков
следует избегать!
Слияние двух линий потока
Слияние двух линий потока, каждая из которых направлена к одному и тому же символу на схеме
На рисунках 5.1–5.3 приведены примеры, поясняющие использование раз- личных графических символов на блок-схемах алгоритмов.