конспект. Конспект. Курс лекций по дисциплине процедурное программирование
Скачать 1.95 Mb.
|
Когда мы разбирали понятие массива, то при объявлении мы задавали массиву определенный постоянный размер. Возможно, кто-то пробовал делать так: int n = 10; int arr[n]; Но, как уже было сказано — при объявлении статического массива, его размером должна являться числовая константа, а не переменная. В большинстве случаев, целесообразно выделять определенное количество памяти для массива, значение которого изначально неизвестно. Например, необходимо создать динамический массив из N элементов, где значение N задается пользователем. Мы уже учились выделять память для переменных, используя указатели. Выделение памяти для динамического массива имеет аналогичный принцип. Создание динамического массива #include using namespace std; int main() { int num; // размер массива cout << "Enter integer value: "; cin >> num; // получение от пользователя размера массива int *p_darr = new int[num]; // Выделение памяти для массива for (int i = 0; i < num; i++) { // Заполнение массива и вывод значений его элементов p_darr[i] = i; cout << "Value of " << i << " element = " << p_darr[i] << endl; } delete [] p_darr; // очистка памяти return 0; } Синтаксис выделения памяти для массива имеет вид указатель = new тип[размер] . В качестве размера массива может выступать любое целое положительное значение. Задание 1. Объявите указатель на массив типа double и предложите пользователю выбрать его размер. Далее напишите четыре функции : первая должна выделить память для массива, вторая – заполнить ячейки данными, третья – показать данные на экран, четвертая – освободить занимаемую память. Программа должна предлагать пользователю продолжать работу (создавать новые динамические массивы) или выйти из программы. Задание 2. Объявите указатель на массив типа int и выделите память для 12-ти элементов. Необходимо написать функцию, которая поменяет значения четных и нечетных ячеек массива. Например, есть массив из 4-х элементов: Тема 9. Параметры командной строки в C++ При запуске программы из командной строки, ей можно передавать дополнительные параметры в текстовом виде. Например, следующая команда ping -t 5 google.com Будет отправлять пакеты на адрес google.com с интервалом в 5 секунд. Здесь мы передали программе ping три параметра: «-t», «5» и «google.com», которые программа интерпретирует как задержку между запросами и адрес хоста для обмена пакетами. В программе эти параметры из командной строки можно получить через аргументы функции main при использовании функции main в следующей форме: int main(int argc, char* argv[]) { /* ... */ } Первый аргумент содержит количество параметров командной строки. Второй аргумент — это массив строк, содержащий параметры командной строки. Т.е. первый аргумент указывает количество элементов массива во втором аргументе. Первый элемент массива строк ( argv[0] ) всегда содержит строку, использованную для запуска программы (либо пустую строку). Следующие элементы (от 1 до argc - 1 ) содержат параметры командной строки, если они есть. Элемент массива строк argv[argc] всегда должен содержать 0. Пример 1 #include using namespace std; int main(int argc, char *argv[]) { for (int i = 0; i < argc; i++) { // Выводим список аргументов в цикле cout << "Argument " << i << " : " << argv[i] << endl; } return 0; } Откройте командную строку и запустите оттуда скомпилированную программу. Для получения числовых данных из входных параметров, можно использовать функции atoi и atof. Тема 10. Классы в C++ Весь реальный мир состоит из объектов. Города состоят из районов, в каждом районе есть свои названия улиц, на каждой улице находятся жилые дома, которые также состоят из объектов. Практически любой материальный предмет можно представить в виде совокупности объектов, из которых он состоит. Допустим, что нам нужно написать программу для учета успеваемости студентов. Можно представить группу студентов, как класс языка C++. Назовем его Students Основные понятия Классы в программировании состоят из свойств и методов. Свойства — это любые данные, которыми можно характеризовать объект класса. В нашем случае, объектом класса является студент, а его свойствами — имя, фамилия, оценки и средний балл. У каждого студента есть имя — name и фамилия last_name . Также, у него есть промежуточные оценки за весь семестр. Эти оценки мы будем записывать в целочисленный массив из пяти элементов. После того, как все пять оценок будут проставлены, определим средний балл успеваемости студента за весь семестр — свойство average_ball Методы — это функции, которые могут выполнять какие-либо действия над данными (свойствами) класса. Добавим в наш класс функцию calculate_average_ball() , которая будет определять средний балл успеваемости ученика. Методы класса — это его функции. Свойства класса — его переменные. class Students { public: // Функция, считающая средний балл void calculate_average_ball() { int sum = 0; // Сумма всех оценок for (int i = 0; i < 5; ++i) { sum += scores[i]; } // считаем среднее арифметическое average_ball = sum / 5.0; } // Имя студента std::string name; // Фамилия std::string last_name; // Пять промежуточных оценок студента int scores[5]; private: // Итоговая оценка за семестр float average_ball; }; Функция calculate_average_ball() просто делит сумму всех промежуточных оценок на их количество. Модификаторы доступа public и private Все свойства и методы классов имеют права доступа. По умолчанию, все содержимое класса является доступным для чтения и записи только для него самого. Для того, чтобы разрешить доступ к данным класса извне, используют модификатор доступа public . Все функции и переменные, которые находятся после модификатора public , становятся доступными из всех частей программы. Закрытые данные класса размещаются после модификатора доступа private . Если отсутствует модификатор public , то все функции и переменные, по умолчанию являются закрытыми (как в первом примере). Обычно, приватными делают все свойства класса, а публичными — его методы. Все действия с закрытыми свойствами класса реализуются через его методы. Рассмотрим следующий код. class Students { public: // Установка среднего балла void set_average_ball(float ball) { average_ball = ball; } // Получение среднего балла float get_average_ball() { return average_ball; } std::string name; std::string last_name; int scores[5]; private: float average_ball; }; Мы не можем напрямую обращаться к закрытым данными класса. Работать с этими данными можно только посредством методов этого класса. В примере выше, мы используем функцию get_average_ball() для получения средней оценки студента, и set_average_ball() для выставления этой оценки. Функция set_average_ball() принимает средний балл в качестве параметра и присваивает его значение закрытой переменной average_ball . Функция get_average_ball() просто возвращает значение этой переменной. Программа учета успеваемости студентов Создадим программу, которая будет заниматься учетом успеваемости студентов в группе. Создайте заголовочный файл students.h, в котором будет находиться класс Students /* students.h */ #include class Students { public: // Установка имени студента void set_name(std::string student_name) { name = student_name; } // Получение имени студента std::string get_name() { return name; } // Установка фамилии студента void set_last_name(std::string student_last_name) { last_name = student_last_name; } // Получение фамилии студента std::string get_last_name() { return last_name; } // Установка промежуточных оценок void set_scores(int student_scores[]) { for (int i = 0; i < 5; ++i) { scores[i] = student_scores[i]; } } // Установка среднего балла void set_average_ball(float ball) { average_ball = ball; } // Получение среднего балла float get_average_ball() { return average_ball; } private: // Промежуточные оценки int scores[5]; // Средний балл float average_ball; // Имя std::string name; // Фамилия std::string last_name; }; Мы добавили в наш класс новые методы, а также сделали приватными все его свойства. Функция set_name() сохраняет имя студента в переменной name , а get_name() возвращает значение этой переменной. Принцип работы функций set_last_name() и get_last_name() аналогичен. Функция set_scores() принимает массив с промежуточными оценками и сохраняет их в приватную переменную int scores[5] Теперь создайте файл main.cpp со следующим содержимым. /* main.cpp */ #include #include "students.h" int main() { // Создание объекта класса Student Students student; std::string name; std::string last_name; // Ввод имени с клавиатуры std::cout << "Name: "; getline(std::cin, name); // Ввод фамилии std::cout << "Last name: "; getline(std::cin, last_name); // Сохранение имени и фамилии в объект класса Students student.set_name(name); student.set_last_name(last_name); // Оценки int scores[5]; // Сумма всех оценок int sum = 0; // Ввод промежуточных оценок for (int i = 0; i < 5; ++i) { std::cout << "Score " << i+1 << ": "; std::cin >> scores[i]; // суммирование sum += scores[i]; } // Сохраняем промежуточные оценки в объект класса Student student.set_scores(scores); // Считаем средний балл float average_ball = sum / 5.0; // Сохраняем средний балл в объект класса Students student.set_average_ball(average_ball); // Выводим данные по студенту std::cout << "Average ball for " << student.get_name() << " " << student.get_last_name() << " is " << student.get_average_ball() << std::endl; return 0; } В самом начале программы создается объект класса Students . Дело в том, что сам класс является только описанием его объекта. Класс Students является описанием любого из студентов, у которого есть имя, фамилия и возможность получения оценок. Объект класса Students характеризует конкретного студента. Если мы захотим выставить оценки всем ученикам в группе, то будем создавать новый объект для каждого из них. Использование классов очень хорошо подходит для описания объектов реального мира. После создания объекта student , мы вводим с клавиатуры фамилию, имя и промежуточные оценки для конкретного ученика. Пускай это будет Вася Пупкин, у которого есть пять оценок за семестр — две тройки, две четверки и одна пятерка. Введенные данные мы передаем set-функциям, которые присваивают их закрытым переменным класса. После того, как были введены промежуточные оценки, мы высчитываем средний балл на основе этих оценок, а затем сохраняем это значение в закрытом свойстве average_ball , с помощью функции set_average_ball() Скомпилируйте и запустите программу. Отделение данных от логики Вынесем реализацию всех методов класса в отдельный файл students.cpp. /* students.cpp */ #include #include "students.h" // Установка имени студента void Students::set_name(std::string student_name) { Students::name = student_name; } // Получение имени студента std::string Students::get_name() { return Students::name; } // Установка фамилии студента void Students::set_last_name(std::string student_last_name) { Students::last_name = student_last_name; } // Получение фамилии студента std::string Students::get_last_name() { return Students::last_name; } // Установка промежуточных оценок void Students::set_scores(int scores[]) { for (int i = 0; i < 5; ++i) { Students::scores[i] = scores[i]; } } // Установка среднего балла void Students::set_average_ball(float ball) { Students::average_ball = ball; } // Получение среднего балла float Students::get_average_ball() { return Students::average_ball; } А в заголовочном файле students.h оставим только прототипы этих методов. /* students.h */ #pragma once /* Защита от двойного подключения заголовочного файла */ #include class Students { public: // Установка имени студента void set_name(std::string); // Получение имени студента std::string get_name(); // Установка фамилии студента void set_last_name(std::string); // Получение фамилии студента std::string get_last_name(); // Установка промежуточных оценок void set_scores(int []); // Установка среднего балла void set_average_ball(float); // Получение среднего балла float get_average_ball(); private: // Промежуточные оценки int scores[5]; // Средний балл float average_ball; // Имя std::string name; // Фамилия std::string last_name; }; Такой подход называется абстракцией данных — одного из фундаментальных принципов объектно-ориентированного программирования. К примеру, если кто-то другой захочет использовать наш класс в своем коде, ему не обязательно знать, как именно высчитывается средний балл. Он просто будет использовать функцию calculate_average_ball() из второго примера, не вникая в алгоритм ее работы. Над крупными проектами обычно работает несколько программистов. Каждый из них занимается написанием определенной части продукта. В таких масштабах кода, одному человеку практически нереально запомнить, как работает каждая из внутренних функций проекта. В нашей программе, мы используем оператор потокового вывода cout , не задумываясь о том, как он реализован на низком уровне. Кроме того, отделение данных от логики является хорошим тоном программирования. В начале обучения мы говорили о пространствах имен (namespaces). Каждый класс в C++ использует свое пространство имен. Это сделано для того, чтобы избежать конфликтов при именовании переменных и функций. В файле students.cpp мы используем оператор принадлежности :: перед именем каждой функции. Это делается для того, чтобы указать компилятору, что эти функции принадлежат классу Students Создание объекта через указатель При создании объекта, лучше не копировать память для него, а выделять ее в в куче с помощью указателя. И освобождать ее после того, как мы закончили работу с объектом. Реализуем это в нашей программе, немного изменив содержимое файла main.cpp. /* main.cpp */ #include #include "students.h" int main() { // Выделение памяти для объекта Students Students *student = new Students; std::string name; std::string last_name; // Ввод имени с клавиатуры std::cout << "Name: "; getline(std::cin, name); // Ввод фамилии std::cout << "Last name: "; getline(std::cin, last_name); // Сохранение имени и фамилии в объект класса Students student->set_name(name); student->set_last_name(last_name); // Оценки int scores[5]; // Сумма всех оценок int sum = 0; // Ввод промежуточных оценок for (int i = 0; i < 5; ++i) { std::cout << "Score " << i+1 << ": "; std::cin >> scores[i]; // суммирование sum += scores[i]; } // Сохраняем промежуточные оценки в объект класса Student student->set_scores(scores); // Считаем средний балл float average_ball = sum / 5.0; // Сохраняем средний балл в объект класса Students student->set_average_ball(average_ball); // Выводим данные по студенту std::cout << "Average ball for " << student->get_name() << " " << student->get_last_name() << " is " << student->get_average_ball() << std::endl; // Удаление объекта student из памяти delete student; return 0; } При создании статического объекта, для доступа к его методам и свойствам, используют операция прямого обращения — « . » (символ точки). Если же память для объекта выделяется посредством указателя, то для доступа к его методам и свойствам используется оператор косвенного обращения — « -> ». Конструктор и деструктор класса Конструктор класса — это специальная функция, которая автоматически вызывается сразу после создания объекта этого класса. Он не имеет типа возвращаемого значения и должен называться также, как класс, в котором он находится. По умолчанию, заполним двойками массив с промежуточными оценками студента. class Students { // Имя студента std::string name; // Фамилия std::string last_name; // Пять промежуточных оценок студента int scores[5]; // Итоговая оценка за семестр float average_ball; }; class Students { public: // Конструктор класса Students Students(int default_score) { for (int i = 0; i < 5; ++i) { scores[i] = default_score; } } private: int scores[5]; }; int main() { // Передаем двойку в конструктор Students *student = new Students(2); return 0; } Мы можем исправить двойки, если ученик будет хорошо себя вести, и вовремя сдавать домашние задания. А на «нет» и суда нет :-) Деструктор класса вызывается при уничтожении объекта. Имя деструктора аналогично имени конструктора, только в начале ставится знак тильды . Деструктор не имеет входных параметров. #include class Students { public: // Деструктор Students() { std::cout << "Memory has been cleaned. Good bye." << std::endl; } }; int main() { Students *student = new Students; // Уничтожение объекта delete student; return 0; } |