Дата: Воскресенье, 05.04.2015, 18:42 | Сообщение # 1
Агро-Разработчик
[ Легенда Зоны ]
C/С++
Си++ (англ. C++) — компилируемый строго типизированный язык программирования общего назначения. Поддерживает разные парадигмы программирования: процедурную, обобщённую, функциональную; наибольшее внимание уделено поддержке объектно-ориентированного программирования.
IP-адрес: Страна: Российская Федерация Город: Москва Дата регистрации: 25.10.2014
Дата: Воскресенье, 05.04.2015, 22:32 | Сообщение # 2
Агро-Разработчик
[ Легенда Зоны ]
C++ с нуля - урок 1 (первая программа)
В качестве среды разработки под Windows автор советует использовать Visual Studio 2008 (800 Мб), если у вас Windows XP, Visual Studio 2013 (6.5 Гб) для Windows 7 и выше, или Dev C++ 5 (41 Мб) для тех, у кого слишком медленный интернет для загрузки Visual Studio. Пользователям Unix-подобных систем, потребуется компилятор GCC и любой текстовый редактор, например Vim.
После того, как вы установили все необходимое, приступим к написанию первой программы.Создание проекта
Создание проекта Для Visual Studio Откройте меню «Файл → Создать → Проект». Перейдите на вкладку «Общие» и выберите «Пустой проект». Придумайте проекту любое название, например «lesson1» и нажмите «OK».
В окне обозревателя решений (обычно он находится в левом верхнем углу) щелкните правой кнопкой на папке «файлы исходного кода». В диалоговом окне выберите пункт меню «Добавить → Создать элемент». Введите название для нового файла — main.cpp и нажмите кнопку «Добавить».
Для GCC Создайте пустой файл и откройте его любым текстовым редактором, в котором есть подсветка синтаксиса.
Код первой программы Наберите следующий код:
#include <iostream> using namespace std;
int main() { cout << "Hello, world!" << endl; system("pause"); // Только для тех, у кого MS Visual Studio return 0; } Описание синтаксиса Директива #include используется для подключения других файлов в код. Строка #include <iostream>, будет заменена содержимым файла «iostream.h», который находится в стандартной библиотеке языка и отвечает за ввод и вывод данных на экран.
Содержимое второй строки — using namespace std; указывает на то, что мы используем по умолчанию пространство имен с названием «std». Все то, что находится внутри фигурных скобок функции int main() {} будет автоматически выполняться после запуска программы.
Строка cout << "Hello, world!" << endl; говорит программе выводить сообщение с текстом«Hello, world» на экран.
Оператор cout предназначен для вывода текста на экран командной строки. После него ставятся две угловые кавычки (<<). Далее идет текст, который должен выводиться. Он помещается в двойные кавычки. Оператор endl переводит строку на уровень ниже.
Если в процессе выполнения произойдет какой-либо сбой, то будет сгенерирован код ошибки, отличный от нуля. Если же работа программы завершилась без сбоев, то код ошибки будет равен нулю. Команда return 0 необходима для того, чтобы передать операционной системе сообщение об удачном завершении программы.
— В конце каждой команды ставится точка с запятой.
Компиляция и запуск Теперь скомпилируйте и запустите программу. Тем, кто пользуется MS Visual Studio, нужно нажать сочетание клавиш «Ctrl+F5». Пользователям GCC нужно выполнить следующие команды:
c++ имя_файла.cpp -o имя_выходного_бинарника # компиляция кода ./имя_выходного_бинарника # запуск программы
Если программа собралась с первого раза, то хорошо. Если компилятор говорит о наличии ошибок, значит вы что-то сделали неправильно.
Прочитайте текст ошибки и попробуйте ее исправить своими силами. Если не получится, напишите о вашей проблеме в комментариях.
В качестве домашнего задания, переделайте эту программу так, чтобы вместо, сообщения «Hello, World» выводилось сообщение «Hello, User».
IP-адрес: Страна: Российская Федерация Город: Москва Дата регистрации: 25.10.2014
Дата: Воскресенье, 05.04.2015, 22:32 | Сообщение # 3
Агро-Разработчик
[ Легенда Зоны ]
C++ с нуля - урок 2 (переменные и типы данных)
Из школьного курса математики мы все знаем, что такое переменные. В программировании принципы довольно схожи. Переменная — это «ячейка» оперативной памяти компьютера, в которой может храниться какая-либо информация.
В программировании переменная, как и в математике, может иметь название, состоящее из одной латинской буквы, но также может состоять из нескольких символов, целого слова или нескольких слов.
Типы данных В языке С++ все переменные имеют определенный тип данных. Например, переменная, имеющая целочисленный тип не может содержать ничего кроме целых чисел, а переменная с плавающей точкой — только дробные числа.
Тип данных присваивается переменной при ее объявлении или инициализации. Ниже приведены основные типы данных языка C++, которые нам понадобятся.
Основные типы данных в C++
int
— целочисленный тип данных.
float
— тип данных с плавающей запятой.
double
— тип данных с плавающей запятой двойной точности.
char
— символьный тип данных.
bool
— логический тип данных. Объявление переменной Объявление переменной в C++ происходит таким образом: сначала указывается тип данных для этой переменной а затем название этой переменной.
Пример объявления переменных int a; // объявление переменной a целого типа. float b; // объявление переменной b типа данных с плавающей запятой. double c = 14.2; // инициализация переменной типа double. char d = 's'; // инициализация переменной типа char. bool k = true; // инициализация логической переменной k.
Заметьте, что в C++ оператор присваивания (=) — не является знаком равенства и не может использоваться для сравнения значений. Оператор равенства записывается как «двойное равно» — ==.
Присваивание используется для сохранения определенного значение в переменной. Например, запись вида a = 10 задает переменной a значение числа 10. Простой калькулятор на C++ Сейчас мы напишем простую программу-калькулятор, которая будет принимать от пользователя два целых числа, а затем определять их сумму:
#include <iostream> using namespace std;
int main() { setlocale(0, ""); /*7*/ int a, b; // объявление двух переменных a и b целого типа данных. cout << "Введите первое число: "; cin >> a; // пользователь присваивает переменной a какое-либо значение. cout << "Введите второе число: "; cin >> b; /*12*/ int c = a + b; // новой переменной c присваиваем значение суммы введенных пользователем данных. cout << "Сумма чисел = " << c << endl; // вывод ответа. return 0; } Разбор кода В 7-й строке кода программы мы объявляем переменные «a» и «b» целого типа int. В следующей строке кода выводится сообщение пользователю, чтобы он ввел с клавиатуры первое число.
В 9-й строке стоит еще незнакомый вам оператор — cin. Этот оператор просит пользователя ввести значение переменной «b» с клавиатуры. Аналогичным образом задается значение переменной «c».
В 12-й строке мы производим инициализацию переменной «c» суммой переменных «a» и «b». Далее находится уже знакомый вам оператор cout, который выводит на экран строку и значение переменной «c».
При выводе переменных, они не заключаются в кавычки, в отличие от строк. Домашнее задание Попробуйте провести несколько экспериментов с программой — сделайте аналогичный пример с умножением или вычитанием переменных. Не бойтесь издеваться над программным кодом, потому что ошибки — неотъемлемая часть обучения любому делу. И не забываем про точки с запятой.
IP-адрес: Страна: Российская Федерация Город: Москва Дата регистрации: 25.10.2014
Дата: Воскресенье, 05.04.2015, 22:33 | Сообщение # 4
Агро-Разработчик
[ Легенда Зоны ]
С++ с нуля - урок 3 (конструкция ветвления)
Встречаются ситуации, когда программе нужно выбрать, какую операцию ей выполнить, в зависимости от определенного условия.
К примеру, мы вводим с клавиатуры целое число. Если это число больше десяти, то программа должна выполнить одно действие, иначе — другое. Реализуем этот алгоритм на C++ с помощью конструкции ветвления.
if (num < 10) { // Если введенное число меньше 10. cout << "Это число меньше 10." << endl; } else { // иначе cout << "Это число больше либо равно 10." << endl; } return 0; } Если вы запустите эту программу, то при вводе числа, меньшего десяти, будет выводиться соответствующее сообщение.
Если введенное число окажется большим, либо равным десяти — отобразится другое сообщение.
Оператор if Оператор if служит для того, чтобы выполнить какую-либо операцию в том случае, когда условие является верным. Условная конструкция в С++ всегда записывается в круглых скобках после оператора if.
Внутри фигурных скобок указывается тело условия. Если условие выполнится, то начнется выполнение всех команд, которые находятся между фигурными скобками.
Пример конструкции ветвления if (num < 10) { // Если введенное число меньше 10. cout << "Это число меньше 10." << endl; } else { // иначе cout << "Это число больше либо равно 10." << endl; } Здесь говорится: «Если переменная num меньше 10 — вывести соответствующее сообщение. Иначе, вывести другое сообщение».
Усовершенствуем программу так, чтобы она выводила сообщение, о том, что переменная num равна десяти:
if (num < 10) { // Если введенное число меньше 10. cout << "Это число меньше 10." << endl; } else if (num == 10) { cout << "Это число равно 10." << endl; } else { // иначе cout << "Это число больше 10." << endl; } Здесь мы проверяем три условия:
Первое — когда введенное число меньше 10-ти
Второе — когда число равно 10-ти
И третье — когда число больше десяти Заметьте, что во втором условии, при проверке равенства, мы используем оператор равенства — ==, а не оператор присваивания, потому что мы не изменяем значение переменной при проверке, а сравниваем ее текущее значение с числом 10.
Если поставить оператор присваивания в условии, то при проверке условия, значение переменной изменится, после чего это условие выполнится. Каждому оператору if соответствует только один оператор else. Совокупность этих операторов — else if означает, что если не выполнилось предыдущее условие, то проверить данное. Если ни одно из условий не верно, то выполняется тело оператора else.
Если после оператора if, else или их связки else if должна выполняться только одна команда, то фигурные скобки можно не ставить. Предыдущую программу можно записать следующим образом:
if (num < 10) // Если введенное число меньше 10. cout << "Это число меньше 10." << endl; else if (num == 10) cout << "Это число равно 10." << endl; else // иначе cout << "Это число больше 10." << endl;
return 0; }
Такой метод записи выглядит более компактно. Если при выполнении условия нам требуется выполнить более одной команды, то фигурные скобки необходимы. Например:
if (num < 10) { // Если введенное число меньше 10. cout << "Это число меньше 10." << endl; k = 1; } else if (num == 10) { cout << "Это число равно 10." << endl; k = 2; } else { // иначе cout << "Это число больше 10." << endl; k = 3; }
cout << "k = " << k << endl; return 0; }
Данная программа проверяет значение переменной num. Если она меньше 10, то присваивает переменной k значение единицы. Если переменная num равна десяти, то присваивает переменной kзначение двойки. В противном случае — значение тройки. После выполнения ветвления, значение переменной k выводится на экран.
Хорошенько потренируйтесь, попробуйте придумать свой пример с ветвлением. Когда поймете, что поняли эту тему, можете смело приступать к следующему уроку.
IP-адрес: Страна: Российская Федерация Город: Москва Дата регистрации: 25.10.2014
Иногда необходимо повторять одно и то же действие несколько раз подряд. Для этого используют циклы. В этом уроке мы научимся программировать циклы на C++, после чего посчитаем сумму всех чисел от 1 до 1000.
Цикл for Если мы знаем точное количество действий (итераций) цикла, то можем использовать цикл for. Синтаксис его выглядит примерно так:
for (действие до начала цикла; условие продолжения цикла; действия в конце каждой итерации цикла) { инструкция цикла; инструкция цикла 2; инструкция цикла N; } Итерацией цикла называется один проход этого цикла
Существует частный случай этой записи, который мы сегодня и разберем:
for (счетчик = значение; счетчик < значение; шаг цикла) { тело цикла; } Счетчик цикла — это переменная, в которой хранится количество проходов данного цикла.
Описание синтаксиса 1. Сначала присваивается первоначальное значение счетчику, после чего ставится точка с запятой.
2. Затем задается конечное конечное значение счетчика цикла. После того, как значение счетчика достигнет указанного предела, цикл завершится. Снова ставим точку с запятой.
3. Задаем шаг цикла. Шаг цикла — это значение, на которое будет увеличиваться или уменьшаться счетчик цикла при каждом проходе. Пример кода Напишем программу, которая будет считать сумму всех чисел от 1 до 1000.
#include <iostream> using namespace std;
int main() { int i; // счетчик цикла int sum = 0; // сумма чисел от 1 до 1000. setlocale(0, ""); for (i = 1; i <= 1000; i++) // задаем начальное значение 1, конечное 1000 и задаем шаг цикла - 1. { sum = sum + i; } cout << "Сумма чисел от 1 до 1000 = " << sum << endl; return 0; } Если мы скомпилируем этот код и запустим программу, то она покажет нам ответ: 500500. Это и есть сумма всех целых чисел от 1 до 1000. Если считать это вручную, понадобится очень много времени и сил. Цикл выполнил всю рутинную работу за нас.
Заметьте, что конечное значение счетчика я задал нестрогим неравенством ( <= — меньше либо равно), поскольку, если бы я поставил знак меньше, то цикл произвел бы 999 итераций, т.е. на одну меньше, чем требуется. Это довольно важный момент, т.к. здесь новички часто допускают ошибки, особенно при работе с массивами (о них будет рассказано в следующем уроке). Значение шага цикла я задал равное единице. i++ — это тоже самое, что и i = i + 1.
В теле цикла, при каждом проходе программа увеличивает значение переменной sum на i. Еще один очень важный момент — в начале программы я присвоил переменной sum значение нуля. Если бы я этого не сделал, программа вылетела вы в сегфолт. При объявлении переменной без ее инициализации что эта переменная будет хранить «мусор».
Естественно к мусору мы ничего прибавить не можем. Некоторые компиляторы, такие как gcc, инициализирует переменную нулем при ее объявлении.
Цикл while Когда мы не знаем, сколько итераций должен произвести цикл, нам понадобится цикл while или do...while. Синтаксис цикла while в C++ выглядит следующим образом.
while (Условие) { Тело цикла; } Данный цикл будет выполняться, пока условие, указанное в круглых скобках является истиной. Решим ту же задачу с помощью цикла while. Хотя здесь мы точно знаем, сколько итераций должен выполнить цикл, очень часто бывают ситуации, когда это значение неизвестно.
Ниже приведен исходный код программы, считающей сумму всех целых чисел от 1 до 1000.
#include <iostream> using namespace std;
int main() { setlocale(0, ""); int i = 0; // инициализируем счетчик цикла. int sum = 0; // инициализируем счетчик суммы. while (i < 1000) { i++; sum += i; } cout << "Сумма чисел от 1 до 1000 = " << sum << endl; return 0; }
После компиляции программа выдаст результат, аналогичный результату работы предыдущей программы. Но поясним несколько важных моментов. Я задал строгое неравенство в условии цикла и инициализировал счетчик i нулем, так как в цикле while происходит на одну итерацию больше, потому он будет выполняться, до тех пор, пока значение счетчика перестает удовлетворять условию, но данная итерация все равно выполнится. Если бы мы поставили нестрогое неравенство, то цикл бы закончился, когда переменная i стала бы равна 1001 и выполнилось бы на одну итерацию больше.
Теперь давайте рассмотрим по порядку исходный код нашей программы. Сначала мы инициализируем счетчик цикла и переменную, хранящую сумму чисел.
В данном случае мы обязательно должны присвоить счетчику цикла какое-либо значение, т.к. в предыдущей программе мы это значение присваивали внутри цикла for, здесь же, если мы не инициализируем счетчик цикла, то в него попадет «мусор» и компилятор в лучшем случае выдаст нам ошибку, а в худшем, если программа соберется — сегфолт практически неизбежен.
Затем мы описываем условие цикла — «пока переменная i меньше 1000 — выполняй цикл». При каждой итерации цикла значение переменной-счетчика i увеличивается на единицу внутри цикла.
Когда выполнится 1000 итераций цикла, счетчик станет равным 999 и следующая итерация уже не выполнится, поскольку 1000 не меньше 1000. Выражение sum += i является укороченной записьюsum = sum + i.
После окончания выполнения цикла, выводим сообщение с ответом.
Цикл do while Цикл do while очень похож на цикл while. Единственное их различие в том, что при выполнении цикла do while один проход цикла будет выполнен независимо от условия. Решение задачи на поиск суммы чисел от 1 до 1000, с применением цикла do while.
#include <iostream> using namespace std;
int main () { setlocale(0, ""); int i = 0; // инициализируем счетчик цикла. int sum = 0; // инициализируем счетчик суммы. do {// выполняем цикл. i++; sum += i; } while (i < 1000); // пока выполняется условие. cout << "Сумма чисел от 1 до 1000 = " << sum << endl; return 0; }
Принципиального отличия нет, но если присвоить переменной i значение, большее, чем 1000, то цикл все равно выполнит хотя бы один проход.
Попрактикуйтесь, поэкспериментируйте над собственными примерами задач. Циклы — очень важная вещь, поэтому им стоит уделить побольше внимания. Когда поймете, как работают циклы — можете смело переходить к изучению следующего урока.
IP-адрес: Страна: Российская Федерация Город: Москва Дата регистрации: 25.10.2014
Сегодня мы с поговорим о массивах. Вы уже знаете, что переменная — это ячейка в памяти компьютера, где может храниться одно единственное значение. Массив — это область памяти, где могут последовательно храниться несколько значений.
Возьмем группу студентов из десяти человек. У каждого из них есть фамилия. Создавать отдельную переменную для каждого студента — не рационально. Создадим массив, в котором будут храниться фамилии всех студентов.
Пример инициализации массива string students[10] = { "Иванов", "Петров", "Сидоров", "Ахмедов", "Ерошкин", "Выхин", "Андеев", "Вин Дизель", "Картошкин", "Чубайс" }; Описание синтаксиса Массив создается почти так же, как и обычная переменная. Для хранения десяти фамилий нам нужен массив, состоящий из 10 элементов. Количество элементов массива задается при его объявлении и заключается в квадратные скобки.
Чтобы описать элементы массива сразу при его создании, можно использовать фигурные скобки. В фигурных скобках значения элементов массива перечисляются через запятую. В конце закрывающей фигурной скобки ставится точка с запятой.
Попробуем вывести наш массив на экран с помощью оператора cout.
#include <iostream> #include <string>
int main() { std::string students[10] = { "Иванов", "Петров", "Сидоров", "Ахмедов", "Ерошкин", "Выхин", "Андеев", "Вин Дизель", "Картошкин", "Чубайс" }; std::cout << students << std::endl; // Пытаемся вывести весь массив непосредственно return 0; } Скомпилируйте этот код и посмотрите, на результат работы программы. Готово? А теперь запустите программу еще раз и сравните с предыдущим результатом. В моей операционной системе вывод был следующим:
Первый вывод: 0x7ffff8b85820
Второй вывод: 0x7fff7a335f90
Третий вывод: 0x7ffff847eb40
Мы видим, что выводится адрес этого массива в оперативной памяти, а никакие не «Иванов» и «Петров».
Дело в том, что при создании переменной, ей выделяется определенное место в памяти. Если мы объявляем переменную типа int, то на машинном уровне она описывается двумя параметрами — ее адресом и размером хранимых данных.
Массивы в памяти хранятся таким же образом. Массив типа int из 10 элементов описывается с помощью адреса его первого элемента и количества байт, которое может вместить этот массив. Если для хранения одного целого числа выделяется 4 байта, то для массива из десяти целых чисел будет выделено 40 байт.
Так почему же, при повторном запуске программы, адреса различаются? Это сделано для защиты от атак переполнения буфера. Такая технология называется рандомизацией адресного пространства и реализована в большинстве популярных ОС.
Попробуем вывести первый элемент массива — фамилию студента Иванова.
#include <iostream> #include <string>
int main() { std::string students[10] = { "Иванов", "Петров", "Сидоров", "Ахмедов", "Ерошкин", "Выхин", "Андеев", "Вин Дизель", "Картошкин", "Чубайс" }; std::cout << students[0] << std::endl; return 0; } Смотрим, компилируем, запускаем. Убедились, что вывелся именно «Иванов». Заметьте, что нумерация элементов массива в C++ начинается с нуля. Следовательно, фамилия первого студента находится в students[0], а фамилия последнего — в students[9].
В большинстве языков программирования нумерация элементов массива также начинается с нуля.
Попробуем вывести список всех студентов. Но сначала подумаем, а что если бы вместо группы из десяти студентов, была бы кафедра их ста, факультет из тысячи, или даже весь университет? Ну не будем же мы писать десятки тысяч строк с cout?
Конечно же нет! Мы возьмем на вооружение циклы, о которых был написан предыдущий урок.
Вывод элементов массива через цикл #include <iostream> #include <string>
int main() { std::string students[10] = { "Иванов", "Петров", "Сидоров", "Ахмедов", "Ерошкин", "Выхин", "Андеев", "Вин Дизель", "Картошкин", "Чубайс" }; for (int i = 0; i < 10; i++) { std::cout << students << std::endl; }
return 0; }
Если бы нам пришлось выводить массив из нескольких тысяч фамилий, то мы бы просто увеличили конечное значение счетчика цикла — строку for (...; i < 10; ...) заменили на for (...; i < 10000; ...).
Заметьте что счетчик нашего цикла начинается с нуля, а заканчивается девяткой. Если вместо оператора строгого неравенства — i < 10 использовать оператор «меньше, либо равно» — i <= 10, то на последней итерации программа обратится к несуществующему элементу массива —students[10]. Это может привести к ошибкам сегментации и аварийному завершению программы. Будьте внимательны — подобные ошибки бывает сложно отловить.
Массив, как и любую переменную можно не заполнять значениями при объявлении.
Объявление массива без инициализации string students[10]; // или string teachers[5];
Элементы такого массива обычно содержат в себе «мусор» из выделенной, но еще не инициализированной, памяти. Некоторые компиляторы, такие как GCC, заполняют все элементы массива нулями при его создании.
При создании статического массива, для указания его размера может использоваться только константа. Размер выделяемой памяти определяется на этапе компиляции и не может изменяться в процессе выполнения.
int n; cin >> n; string students[n]; /* Неверно */
Выделение памяти в процессе выполнения возможно при работе с динамическими массивами. Но о них немного позже.
Заполним с клавиатуры пустой массив из 10 элементов.
Заполнение массива с клавиатуры #include <iostream> #include <string>
using std::cout; using std::cin; using std::endl;
int main() { int arr[10];
// Заполняем массив с клавиатуры for (int i = 0; i < 10; i++) { cout << "[" << i + 1 << "]" << ": "; cin >> arr; }
// И выводим заполненный массив. cout << "\nВаш массив: ";
for (int i = 0; i < 10; ++i) { cout << arr << " "; }
cout << endl;
return 0; }
Скомпилируем эту программу и проверим ее работу.
Если у вас возникают проблемы при компиляции исходников из уроков — внимательно прочитайте ошибку компилятора, попробуйте проанализировать и исправить ее. Если вы нашли ошибку в коде — напишите об этом в комментариях к уроку.
Массивы — очень важная вещь в программировании. Автор советует вам хорошо попрактиковаться в работе с ними.
IP-адрес: Страна: Российская Федерация Город: Москва Дата регистрации: 25.10.2014
Сегодня мы поговорим о функциях в C++. Очень часто в программировании необходимо выполнять одни и те же действия. Например, мы хотим выводить пользователю сообщения об ошибке в разных местах программы, если он ввел неверное значение. без функций это выглядело бы так:
По сути, после компиляции не будет никакой разницы для процессора, как для первого кода, так и для второго. Но ведь такую проверку пароля мы может делать в нашей программе довольно много раз. И тогда получается копипаста и код становится нечитаемым. Функции — один из самых важных компонентов языка C++.
Любая функция имеет тип, также, как и любая переменная.
Функция может возвращать значение, тип которого в большинстве случаев аналогично типу самой функции.
Если функция не возвращает никакого значения, то она должна иметь тип void (такие функции иногда называют процедурами)
При объявлении функции, после ее типа должно находиться имя функции и две круглые скобки — открывающая и закрывающая, внутри которых могут находиться один или несколько аргументов функции, которых также может не быть вообще.
после списка аргументов функции ставится открывающая фигурная скобка, после которой находится само тело функции.
В конце тела функции обязательно ставится закрывающая фигурная скобка.
Пример построения функции #include <iostream> using namespace std;
int main() { function_name(); // Вызов функции return 0; } Перед вами тривиальная программа, Hello, world, только реализованная с использованием функций.
Если мы хотим вывести «Hello, world» где-то еще, нам просто нужно вызвать соответствующую функцию. В данном случае это делается так: function_name();. Вызов функции имеет вид имени функции с последующими круглыми скобками. Эти скобки могут быть пустыми, если функция не имеет аргументов. Если же аргументы в самой функции есть, их необходимо указать в круглых скобках.
Также существует такое понятие, как параметры функции по умолчанию. Такие параметры можно не указывать при вызове функции, т.к. они примут значение по умолчанию, указанно после знака присваивания после данного параметра и списке всех параметров функции.
В предыдущих примерах мы использовали функции типа void, которые не возвращают никакого значения. Как многие уже догадались, оператор return используется для возвращения вычисляемого функцией значения.
Рассмотрим пример функции, возвращающей значение на примере проверки пароля.
int main() { string user_pass; cout << "Введите пароль: "; getline (cin, user_pass); string error_msg = check_pass (user_pass); cout << error_msg << endl; return 0; } В данном случае функция check_pass имеет тип string, следовательно она будет возвращать только значение типа string, иными словами говоря строку. Давайте рассмотрим алгоритм работы этой программы.
Самой первой выполняется функция main(), которая должна присутствовать в каждой программе. Теперь мы объявляем переменную user_pass типа string, затем выводим пользователю сообщение «Введите пароль», который после ввода попадает в строку user_pass. А вот дальше начинает работать наша собственная функция check_pass().
В качестве аргумента этой функции передается строка, введенная пользователем.
Аргумент функции — это, если сказать простым языком переменные или константы вызывающей функции, которые будет использовать вызываемая функция.
При объявлении функций создается формальный параметр, имя которого может отличаться от параметра, передаваемого при вызове этой функции. Но типы формальных параметров и передаваемых функии аргументов в большинстве случаев должны быть аналогичны.
После того, как произошел вызов функции check_pass(_, начинает работать данная функция. Если функцию нигде не вызвать, то этот код будет проигнорирован программой. Итак, мы передали в качестве аргумента строку, которую ввел пользователь.
Теперь эта строка в полном распоряжении функции (хочу обратить Ваше внимание на то, что переменные и константы, объявленные в разных функциях независимы друг от друга, они даже могут иметь одинаковые имена. В следующих уроках я расскажу о том, что такое область видимости, локальные и глобальные переменные).
Теперь мы проверяем, правильный ли пароль ввел пользователь или нет. если пользователь ввел правильный пароль, присваиваем переменной error_message соответствующее значение. если нет, то сообщение об ошибке.
После этой проверки мы возвращаем переменную error_message. На этом работа нашей функции закончена. А теперь, в функции main(), то значение, которое возвратила наша функция мы присваиваем переменной error_msg и выводим это значение (строку) на экран терминала.
Также, можно организовать повторный ввод пароля с помощью рекурсии (о ней мы еще поговорим). Если объяснять вкратце, рекурсия — это когда функция вызывает сама себя. Смотрите еще один пример:
Функции очень сильно облегчают работу программисту и намного повышают читаемость и понятность кода, в том числе и для самого разработчика (не удивляйтесь этому, т. к. если вы откроете код, написанный вами полгода назад,не сразу поймете соль, поверьте на слово).
Не расстраивайтесь, если не сразу поймете все аспекты функций в C++, т. к. это довольно сложная тема и мы еще будем разбирать примеры с функциями в следующих уроках.
Совет: не бойтесь экспериментировать, это очень хорошая практика, а после прочтения данной статьи порешайте элементарные задачи, но с использованием функций. Это будет очень полезно для вас.
Если Вы найдете какие-либо ошибки в моем коде, обязательно напишите об этом в комментариях. здесь же можно задавать все вопросы.
IP-адрес: Страна: Российская Федерация Город: Москва Дата регистрации: 25.10.2014
При выполнении любой программы, все необходимые для ее работы данные должныбыть загружены в оперативную память компьютера. Для обращения к переменным, находящимся в памяти, используются специальные адреса, которые записываются в шестнадцатеричном виде, например 0x100 или 0x200.
Если переменных в памяти потребуется слишком большое количество, которое не сможет вместить в себя сама аппаратная часть, произойдет перегрузка системы или её зависание.массиву данных через указатель, который будет содержать адрес начала диапазона ячеек памяти, хранящих этот массив.
После того, как этот массив станет не нужен для выполнения остальной части программы, мы просто освободим память по адресу этого указателя, и она вновь станет доступно для других переменных.
Ниже приведен конкретный пример обращения к переменным через указатель и напрямую.
Пример использования статических переменных #include <iostream> using namespace std;
int main() { int a; // Объявление статической переменной int b = 5; // Инициализация статической переменной b
a = 10; b = a + b; cout << "b is " << b << endl; return 0; } Пример использования динамических переменных #include <iostream> using namespace std;
int main() { int *a = new int; // Объявление указателя для переменной типа int int *b = new int(5); // Инициализация указателя
*a = 10; *b = *a + *b;
cout << "b is " << *b << endl;
delete b; delete a;
return 0; } Синтаксис первого примера вам уже должен быть знаком. Мы объявляем/инициализируем статичные переменные a и b, после чего выполняем различные операции напрямую с ними.
Во втором примере мы оперируем динамическими переменными посредством указателей. Рассмотрим общий синтаксис указателей в C++.
Выделение памяти осуществляется с помощью оператора new и имеет вид: тип_данных *имя_указателя = new тип_данных;, например int *a = new int;. После удачного выполнения такой операции, в оперативной памяти компьютера происходит выделение диапазона ячеек, необходимого для хранения переменной типа int.
Логично предположить, что для разных типов данных выделяется разное количество памяти. Следует быть особенно осторожным при работе с памятью, потому что именно ошибки программы, вызванные утечкой памяти, являются одними из самых трудно находимых. На отладку программы в поисках одной ничтожной ошибки, может уйти час, день, неделя, в зависимости от упорности разработчика и объема кода.
Инициализация значения, находящегося по адресу указателя выполняется схожим образом, только в конце ставятся круглые скобки с нужным значением: тип данных *имя_указателя = new тип_данных(значение). В нашем примере это int *b = new int(5).
Для того, чтобы получить адрес в памяти, на который ссылается указатель, используется имя переменной-указателя с префиксом &. перед ним (не путать со знаком ссылки в C++).
Например, чтобы вывести на экран адрес ячейки памяти, на который ссылается указатель b во втором примере, мы пишем cout << "Address of b is " << &b << endl;. В моей системе, я получил значение 0x1aba030. У вас оно может быть другим, потому что адреса в оперативной памяти распределяются таким образом, чтобы максимально уменьшить фрагментацию. Поскольку, в любой системе список запущенных процессов, а также объем и разрядность памяти могут отличаться, система сама распределяет данные для обеспечения минимальной фрагментации.
Для того, чтобы получить значение, которое находится по адресу, на который ссылается указатель, используется префикс *. Данная операция называется разыменованием указателя.
Во втором примере мы выводим на экран значение, которое находится в ячейке памяти (у меня это 0x1aba030): cout << "b is " << *b << endl; . В этом случае необходимо использовать знак *.
Чтобы изменить значение, находящееся по адресу, на который ссылается указатель, нужно также использовать звездочку, например, как во втором примере — *b = *a + *b;.
Когда мы оперируем данными, то используем знак *
Когда мы оперируем адресами, то используем знак & В этих вещах очень часто возникают недопонимания, и кстати, не только у новичков. Многие из тех, кто начинал программировать с того же php, также часто испытывают подобную путаницу при работе с памятью.
Для того, чтобы освободить память, выделенную оператором new, используется оператор delete.
Пример освобождения памяти #include <iostream> using namespace std;
int main() { // Выделение памяти int *a = new int; int *b = new int; float *c = new float;
// ... Любые действия программы
// Освобождение выделенной памяти delete c; delete b; delete a;
return 0; }
При использовании оператора delete для указателя, знак * не используется.
IP-адрес: Страна: Российская Федерация Город: Москва Дата регистрации: 25.10.2014
В пятом уроке мы разобрали понятие массива. При объявлении, мы задавали массиву определенный постоянный размер. Возможно, кто-то из читателей пробовал делать так:
int n = 10; int arr[n];
Но, как уже было сказано — при объявлении статического массива, его размером должна являться числовая константа, а не переменная. В большинстве случаев, целесообразно выделять определенное количество памяти для массива, значение которого изначально неизвестно.
Например, необходимо создать динамический массив из N элементов, где значение N задается пользователем. В предыдущем уроке мы учились выделять память для переменных, используя указатели. Выделение памяти для динамического массива имеет аналогичный принцип.
Создание динамического массива #include <iostream> 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; cout << "Value of " << i << " element is " << p_darr << endl; } delete [] p_darr; // очистка памяти return 0; }
Синтаксис выделения памяти для массива имеет вид указатель = new тип[размер]. В качестве размера массива может выступать любое целое положительное значение.
IP-адрес: Страна: Российская Федерация Город: Москва Дата регистрации: 25.10.2014
При запуске программы из командной строки, ей можно передавать дополнительные параметры в текстовом виде. Например, следующая команда
ping -t 5 google.com
Будет отправлять пакеты на адрес google.com с интервалом в 5 секунд. Здесь мы передали программе ping два параметра — это задержка между запросами и адрес хоста для обмена пакетами.
Эти параметры описываются, как аргументы функции main(). Первый аргумент — это количество параметров, которые были переданы программе. В качестве первого аргумента всегда передается название самого файла программы. Второй аргумент — это массив, хранящий все остальные параметры.
Пример 1.1 #include <iostream> using namespace std;
int main(int argc, char *argv[]) { for (int i = 0; i < argc; i++) { // Выводим список аргументов в цикле cout << "Argument " << i << " : " << argv << endl; } return 0; }
Откройте командную строку и запустите оттуда скомпилированную программу.
Для получения числовых данных из входных параметров, можно использовать функции atoi и atof.
IP-адрес: Страна: Российская Федерация Город: Москва Дата регистрации: 25.10.2014
Весь реальный мир состоит из объектов. Города состоят из районов, в каждом районе есть свои названия улиц, на каждой улице находятся жилые дома, которые также состоят из объектов.
Практически любой материальный предмет можно представить в виде совокупности объектов, из которых он состоит. Допустим, что нам нужно написать программу для учета успеваемости студентов. Можно представить группу студентов, как класс языка C++. Назовем его Students. class Students { // Имя студента std::string name; // Фамилия std::string last_name; // Пять промежуточных оценок студента int scores[5]; // Итоговая оценка за семестр float average_ball; }; Основные понятия Классы в программировании состоят из свойств и методов. Свойства — это любые данные, которыми можно характеризовать объект класса. В нашем случае, объектом класса является студент, а его свойствами — имя, фамилия, оценки и средний балл.
У каждого студента есть имя — 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; } // считаем среднее арифметическое 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 <string>
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 = student_scores; } } // Установка среднего балла 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 со следующим содержимым.
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; // суммирование sum += scores; }
// Сохраняем промежуточные оценки в объект класса 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.
// Установка имени студента 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 = scores; } }
// Установка среднего балла 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 <string>
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.
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; // суммирование sum += scores; } // Сохраняем промежуточные оценки в объект класса 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 { public: // Конструктор класса Students Students(int default_score) { for (int i = 0; i < 5; ++i) { scores = default_score; } }
private: int scores[5]; };
int main() { // Передаем двойку в конструктор Students *student = new Students(2); return 0; }
Мы можем исправить двойки, если ученик будет хорошо себя вести, и вовремя сдавать домашние задания. А на «нет» и суда нет
Деструктор класса вызывается при уничтожении объекта. Имя деструктора аналогично имени конструктора, только в начале ставится знак тильды ~. Деструктор не имеет входных параметров.
#include <iostream>
class Students { public: // Деструктор ~Students() { std::cout << "Memory has been cleaned. Good bye." << std::endl; } };
int main() { Students *student = new Students; // Уничтожение объекта delete student; return 0; }
IP-адрес: Страна: Российская Федерация Город: Москва Дата регистрации: 25.10.2014
В этом уроке мы более детально познакомимся с конструкторами и деструкторами класса, а также научимся работать с файлами в потоковом режиме, с помощью библиотеки fstream. Продолжим написание программы учета оценок.
Конструктор Students Добавим в класс Students конструктор, который будет принимать имя и фамилию ученика, и сохранять эти значения в соответствующих переменных класса.
// Конструктор Students Students::Students(std::string name, std::string last_name) { Students::set_name(name); Students::set_last_name(last_name); } При создании нового объекта, мы должны передать конструктору имя и фамилию студента. Иначе компиляция программы завершится с ошибкой.
std::string name = "Василий"; std::string last_name = "Пупкин";
Students *student = new Students(name, last_name); Теперь добавим прототип конструктора в файл students.h.
/* students.h */ #pragma once /* Защита от двойного подключения заголовочного файла */ #include <string>
class Students { public: // Конструктор класса Students Students(std::string, std::string);
// Установка имени студента 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; }; В файле students.cpp определим сам конструктор.
// Установка имени студента 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[]) { int sum = 0; for (int i = 0; i < 5; ++i) { Students::scores = scores; sum += scores; } }
// Установка среднего балла void Students::set_average_ball(float ball) { Students::average_ball = ball; }
// Получение среднего балла float Students::get_average_ball() { return Students::average_ball; } В main() мы принимаем от пользователя имя и фамилию ученика, и сохраняем их во временных локальных переменных. После этого создаем новый объект класса Students, передавая его конструктору эти переменные.
int main(int argc, char *argv[]) { // Локальная переменная, хранящая имя ученика 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 = new Students(name, last_name);
// Оценки int scores[5]; // Сумма всех оценок int sum = 0;
// Ввод промежуточных оценок for (int i = 0; i < 5; ++i) { std::cout << "Score " << i+1 << ": "; std::cin >> scores; // суммирование sum += scores; } // Сохраняем промежуточные оценки в объект класса Student student->set_scores(scores);
// Считаем средний балл float average_ball = sum / 5.0; // Сохраняем средний балл 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; } Сохранение оценок в файл Чтобы после завершения работы с программой, все данные сохранялись, мы будем записывать их в текстовый файл.
Оценки каждого студента будут находится в отдельной строке. Имя и фамилии будут разделяться пробелами. После имени и фамилии ученика ставится еще один пробел, а затем перечисляются все его оценки.
Пример файла с оценками:
Василий Пупкин 5 4 5 3 3 Иван Сидоров 5 5 3 4 5 Андрей Иванов 5 3 3 3 3 Для работы с файлами мы воспользуемся библиотекой fstream, которая подключается в заголовочном файле с таким же именем.
#include <fstream>
// Запись данных о студенте в файл void Students::save() { std:eek :fstream fout("students.txt", std::ios::app);
for (int i = 0; i < 5; ++i) { fout << Students::scores << " "; }
fout << std::endl; fout.close(); } Переменная fout — это объект класса ofstream, который находится внутри библиотеки fstream. Класс ofstream используется для записи каких-либо данных во внешний файл. Кстати, у него тоже есть конструктор. Он принимает в качестве параметров имя выходного файла и режим записи.
В данном случае, мы используем режим добавления — std::ios:app (англ. append). После завершения работы с файлом, необходимо вызвать метод close() для того, чтобы закрыть файловый дескриптор.
Чтобы сохранить оценки студента, мы будем вызывать только что созданный метод save().
Students student = new Students("Василий", "Пупкин"); student->save();
Деструктор Students Логично было бы сохранять все оценки после того, как работа со студентом закончена. Для этого создадим деструктор класса Students, который будет вызывать метод save() перед уничтожением объекта.
// Деструктор Students Students::~Students() { Students::save(); }
Добавим прототипы деструктора и метода save() в students.h.
/* students.h */ #pragma once /* Защита от двойного подключения заголовочного файла */ #include <string>
class Students { public: // Запись данных о студенте в файл void save(); // Деструктор класса Students ~Students();
// Конструктор класса Students Students(std::string, std::string); // Установка имени студента void set_name(std::string); // Получение имени студента std::string get_name();
// Установка фамилии студента void set_last_name(std::string); // Получение фамилии студента std::string get_last_name();
// Установка промежуточных оценок void set_scores(int []); // Получение массива с промежуточными оценками int *get_scores(); // Получение строки с промежуточными оценками std::string get_scores_str(char);
// Установка среднего балла void set_average_ball(float); // Получение среднего балла float get_average_ball(); private: // Промежуточные оценки int scores[5]; // Средний балл float average_ball; // Имя std::string name; // Фамилия std::string last_name; };
for (int i = 0; i < 5; ++i) { fout << Students::scores << " "; }
fout << std::endl; fout.close(); }
// Конструктор Students Students::Students(std::string name, std::string last_name) { Students::set_name(name); Students::set_last_name(last_name); } // Установка имени студента 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[]) { int sum = 0; for (int i = 0; i < 5; ++i) { Students::scores = scores; sum += scores; }
}
// Получение массива с промежуточными оценками int *Students::get_scores() { return Students::scores; }
// Установка среднего балла void Students::set_average_ball(float ball) { Students::average_ball = ball; }
// Получение среднего балла float Students::get_average_ball() { return Students::average_ball; }
Содержимое main.cpp останется прежним. Скомпилируйте и запустите программу. Перед тем, как приложение завершит свою работу, в директории с исполняемым файлом будет создан новый текстовый файл с оценками — students.txt.
IP-адрес: Страна: Российская Федерация Город: Москва Дата регистрации: 25.10.2014
Вектор в C++ — это замена стандартному динамическому массиву, память для которого выделяется вручную, с помощью оператора new.
Разработчики языка рекомендуют в использовать именно vector вместо ручного выделения памяти для массива. Это позволяет избежать утечек памяти и облегчает работу программисту.
Пример создания вектора #include <iostream> #include <vector>
int main() { // Вектор из 10 элементов типа int std::vector<int> v1(10);
// Вектор из элементов типа float // С неопределенным размером std::vector<float> v2;
// Вектор, состоящий из 10 элементов типа int // По умолчанию все элементы заполняются нулями std::vector<int> v3(10, 0);
return 0; } Управление элементами вектора Создадим вектор, в котором будет содержаться произвольное количество фамилий студентов.
do { std::getline(std::cin, buffer); if (buffer.size() > 0) { // Добавление элемента в конец вектора students.push_back(buffer); } } while (buffer != "");
// Сохраняем количество элементов вектора unsigned int vector_size = students.size();
// Вывод заполненного вектора на экран std::cout << "Ваш вектор." << std::endl; for (int i = 0; i < vector_size; i++) { std::cout << students << std::endl; }
return 0; } Результат работы программы:
Методы класса vector Для добавления нового элемента в конец вектора используется метод push_back(). Количество элементов определяется методом size(). Для доступа к элементам вектора можно использовать квадратные скобки [], также, как и для обычных массивов.
pop_back() — удалить последний элемент
clear() — удалить все элементы вектора
empty() — проверить вектор на пустоту
IP-адрес: Страна: Российская Федерация Город: Москва Дата регистрации: 25.10.2014
Наследование позволяет избежать дублирования лишнего кода при написании классов. Пусть в базе данных ВУЗа должна храниться информация о всех студентах и преподавателях. Представлять все данные в одном классе не получится, поскольку для преподавателей нам понадобится хранить данные, которые для студента не применимы, и наоборот.
Создание базового класса Для решения этой задачи создадим базовый класс human, который будет описывать модель человека. В нем будут храниться имя, фамилия и отчество.
private: std::string name; // имя std::string last_name; // фамилия std::string second_name; // отчество };
#endif // HUMAN_H_INCLUDED Наследование от базового класса Теперь создайте новый класс student, который будет наследником класса human. Поместите его в файл student.h.
class student : public human { public: // Конструктор класса Student student( std::string last_name, std::string name, std::string second_name, std::vector<int> scores ) : human( last_name, name, second_name ) { this->scores = scores; }
// Получение среднего балла студента float get_average_score() { // Общее количество оценок unsigned int count_scores = this->scores.size(); // Сумма всех оценок студента unsigned int sum_scores = 0; // Средний балл float average_score;
for (unsigned int i = 0; i < count_scores; ++i) { sum_scores += this->scores; }
private: // Оценки студента std::vector<int> scores; }; #endif // STUDENT_H_INCLUDED Функция get_average_score вычисляет среднее арифметическое всех оценок студента. Всепубличные свойства и методы класса human будут доступны в классе student.
Конструктор базового класса Для того, чтобы инициализировать конструктор родительского класса (в нашем случае — это сохранение имени, фамилии и отчества ученика), используется следующий синтаксис:
// Конструктор класса Student student( // аргументы конструктора текущего класса ) : human( // инициализация конструктора родительского класса ) { // инициализация конструктора текущего класса } В конструктор класса human мы передаем инициалы человека, которые сохраняются в экземпляре класса. Для класса students, нам необходимо задать еще и список оценок студента. Поэтому конструктор students принимает все аргументы конструктора базового класса, а также дополнительные аргументы для расширения функционала:
// Создание объекта класса student student *stud = new student("Петров", "Иван", "Алексеевич", scores);
// Вывод полного имени студента (используется унаследованный метод класса human) std::cout << stud->get_full_name() << std::endl; // Вывод среднего балла студента std::cout << "Средний балл: " << stud->get_average_score() << std::endl;
return 0; } В этом примере мы написали программу, которая создает объект класса student, сохраняя в нем его имя, фамилию, отчество и список оценок.
После инициализации объекта, происходит вывод полного имени студента с помощью функцииget_full_name. Эта функция была унаследована от базового класса human.
Затем программа вычислияет средний балл студента и выводит его на экран. Этим занимается функция get_average_score, которую мы описали внутри класса student. 21.png Мы реализовали часть функционала для нашей базы данных института (я конечно утрирую, когда оперирую столь серьезными высказываниями про настоящую базу данных
Создание класса-наследника teacher Нужно создать еще один класс, в котором будут храниться данные преподавателей. Дадим ему название — teacher. Как вы уже поняли, мы не будем описывать все методы этого класса с нуля, а просто унаследуем его от класса human. Тогда, не нужно будет реализовывать хранение имени, фамилии и отчества препода. Это уже есть в базовом классе human.
class teacher : public human { // Конструктор класса teacher public: teacher( std::string last_name, std::string name, std::string second_name, // Количество учебных часов за семетр у преподавателя unsigned int work_time ) : human( last_name, name, second_name ) { this->work_time = work_time; }
// Получение количества учебных часов unsigned int get_work_time() { return this->work_time; }
private: // Учебные часы unsigned int work_time; };
#endif // TEACHER_H_INCLUDED
У класса teacher появилось новое свойство — количество учебных часов, отведенное преподавателю на единицу времени (семестр). Весь остальной функционал наследуется от базового класса human. Если бы мы писали все с нуля, то одинакового кода бы получилось в разы больше, и его поддержка усложнилась бы на порядок.
Создание объекта класса teacher Изменим содержимое файла main.cpp, чтобы проверить работу класса teacher.
#include <iostream>
#include "human.h" #include "teacher.h"
int main(int argc, char* argv[]) {
// Количество учебных часов преподавателя unsigned int teacher_work_time = 40;
teacher *tch = new teacher("Васильков", "Петр", "Сергеевич", teacher_work_time);
Если сборка программы прошла без ошибок, то результат работы программы будет таким:
Можно таким же образом создать класс, в котором будут храниться данные обслуживающего персонала или руководящего состава. Наследование используют, когда у каждой группы объектов есть общие параметры, но для каждой из этих групп нужно хранить более кастомные данные.
Также, мы можем создать класс, который будет описывыть студента заочной формы обучения. Его мы унаследовали бы от класса student, добавив какие-либо дополнительные данные.
В класс human можно добавить еще больше свойств, которые будут описывать данные, имеющиеся у любого человека. Например, номер паспорта, дату рождения, прописку и место проживания.
Подобный подход позволяет в разы уменьшить дублирование кода в реальных проектах, и упросить его поддержку.
Когда нужно использовать конструктор Если у класса много свойств — их совсем не обязательно задавать в конструкторе. Для сохранения отдельных свойств класса используют set-функции. Например, для сохранения номера паспорта, можно создать публичный метод set_passport_number(std::string number), который будет принимать значение свойства и сохранять его в объекте, через переменную this.
IP-адрес: Страна: Российская Федерация Город: Москва Дата регистрации: 25.10.2014
Перегрузка функций в C++ используется, когда нужно сделать одно и то же действие с разными типами данных. Для примера, создадим простую функцию max, которая будет определять максимальное из двух целых чисел.
/* Функция max для целых чисел */ int max(int num1, int num2) { if (num1 > num2) return num1; return num2; } В эту функцию мы можем передавать только целочисленные параметры. Для того, чтобы сделать аналог этой функции для чисел с плавающей запятой, выполним перегрузку этой функции:
/* Функция max для чисел с плавающей запятой */ double max(double num1, double num2) { if (num1 > num2) return num1; return num2; } Теперь, когда мы будет вызывать функцию max с целыми параметрами, то вызовется первая функция. А если с дробными — то вторая. Например:
// Здесь будет использоваться первый вариант функции max int imax = max(1, 10); // А здесь - второй double dmax = max(1.0, 20.0);
Задание: попробуйте написать функцию сортировки массива пузырьком для целочисленных массивов. А затем перегрузить эту же функцию для массивов типа double.
IP-адрес: Страна: Российская Федерация Город: Москва Дата регистрации: 25.10.2014