- •Экзамен 374 Предварительные рассуждения Вступительное слово
- •Исторические факты
- •Начнем!
- •Проба пера
- •Открытие сохраненного проекта
- •Вывод данных
- •Типы данных
- •Хороший стиль программирования
- •Переменные и константы
- •Практический пример
- •Ввод данных
- •Например:
- •Пример:
- •Арифметические операции с числами
- •Литералы
- •Некоторые примеры
- •Домашнее задание
- •Напишите программу, которая вводит число из трех цифр, разделяет число на отдельные цифры и печатает их отдельно друг от друга с тремя пробелами между ними. Преобразование типов
- •Перечисляемые типы
- •Типичная ошибка
- •Хороший стиль программирования
- •Типичная ошибка
- •Выражения
- •Оператор if
- •Структура программы
- •Логические операции
- •Структура множественного выбора switch
- •Практический пример
- •Цикл for
- •Практический пример
- •Цикл do-while
- •Домашнее задание
- •Вызов функции
- •Прототипы функций
- •Разбор программы
- •Область видимости
- •Аргументы по умолчанию
- •Встраивание
- •Перегрузка функций
- •Учебный пример перегруженных функций. Иллюстрация перегрузки
- •Результат работы программы
- •Практические примеры
- •Домашнее задание
- •Примеры домашней работы урока 1 Пример №1
- •Как работает программа
- •Пример №2
- •Как работает программа
- •Примеры домашних работ на создание функций Пример №1
- •Как работает программа
- •Пример №2
- •Как работает программа
- •Массивы
- •Объявление массивов
- •Примеры использования массивов
- •Программа 1
- •Программа 2
- •Обратите внимание!
- •Типичная ошибка программирования
- •Типичная ошибка программирования
- •Программа 3
- •Типичная ошибка программирования
- •Замечание по технике программирования
- •Программа 4
- •Программа 5
- •Программа нахождения минимального и максимального элементов массива
- •Сортировка массивов
- •Домашнее задание
- •Что такое указатели?
- •За кулисами...
- •Как работать с указателями?..
- •Зачем нужны указатели?
- •Указатели и Массивы.
- •Примеры задач
- •Пример 1
- •Пример 2
- •Пример 3
- •Указатели - аргументы функций.
- •Ссылочные параметры
- •Примеры решения задач
- •Домашнее задание
- •Операторы свободной памяти new и delete
- •Функции работы со строками из библиотеки обработки строк
- •Пример 1.
- •Пример2
- •Пример 3
- •Пример задачи на новый материал
- •Домашнее задание
- •Двухмерные массивы, как частный случай многомерных массивов
- •Программа.
- •Результаты работы программы.
- •Многомерные динамические массивы
- •Пример на многомерные динамические массивы
- •Домашнее задание
- •Рекурсия
- •Рекурсии или итерации
- •Указатели на функции
- •Пример №1
- •Результат выполнения программы:
- •Пример №2
- •Результат выполнения программы
- •Пример №3
- •Результаты выполнения программы
- •Определения структур
- •Пример #1 на использование структур
- •Пример #2 на использование структур
- •Оператор указателя на структуру
- •Домашнее задание
- •Тест по c Группа ___________________ф. И. О. ______________________
- •Объектно-ориентированное программирование.
- •Наследование (Inheritance).
- •Инкапсуляция (Encapsulation).
- •Определение класса
- •Конструкторы и деструкторы Инициализация объектов класса: конструкторы
- •Основное назначение конструкторов - инициализация объектов.
- •Использование конструкторов с аргументами по умолчанию
- •Если параметры не передаются конструктору, в определении объекта не нужно включать пустые круглые скобки.
- •Использование деструкторов
- •Когда вызываются конструкторы и деструкторы.
- •Домашнее задание
- •Конструктор копирования
- •Синтаксис конструктора копирования
- •Памятка
- •Пример использования конструктора копирования.
- •Перегруженные конструкторы
- •Экскурс в историю
- •Послесловие к примеру
- •Маленькое замечание
- •Домашнее задание
- •Создание класса ''строка''
- •Перегрузка операций.
- •Общие принципы перегрузки операторов.
- •Преобразования, определяемые классом
- •Пример строкового класса с перегруженными операторами
- •Домашнее задание
- •Дружественные функции (Friend Functions)
- •Пример строкового класса с перегруженными операторами и дружественными функциями
- •Перегрузка операторов new и delete
- •Перегрузка оператора индексирования
- •Класс вектор. Часть1.
- •Класс вектор. Часть 2.
- •Класс вектор. Часть 3.
- •Домашнее задание
- •Наследование (Inheritance). Часть 1.
- •Наследование (Inheritance). Часть 2.
- •Множественное наследование (multiple inheritance)
- •Пример множественного наследования
- •Домашнее задание
- •Статические члены данных
- •Раннее и позднее связывание
- •Виртуальные функции
- •Пример.
- •Абстрактные классы
- •Виртуальный базовый класс
- •Практический пример
- •Домашнее задание
- •Потоки ввода-вывода.
- •Iostream.H: stream - поток, "I" - сокр. Input - ввод, "o" - сокр. Output - вывод.
- •Предопределенные потоки.
- •Операции помещения в поток и извлечения из потока.
- •Файловый ввод-вывод с применением потоков.
- •Конструкторы файловых потоков.
- •Функции для открытия и закрытия файлов.
- •Функции для обмена с потоками.
- •Часто применяемые функции потока.
- •Ввод/вывод массива в/из файл(-а).
- •Практический пример: перекодировка файла.
- •Домашнее задание
- •Немного о файлах...
- •И снова файлы...
- •Пример "Телефонная книга"
- •Файл abonent.H
- •Форматирование данных при обменах с потоками.
- •Состояние потока.
- •Использование аргументов командной строки.
- •Ввод/вывод в с.
- •Домашнее задание
- •Определение шаблонов функций
- •Переопределение шаблонов функций
- •Шаблоны классов
- •Шаблонный класс вектор
- •Шаблонный класс вектор
- •Шаблонный класс вектор
- •Введение
- •Обработка исключительных ситуаций
- •Практический пример
- •Программа
- •Домашнее задание
- •Экзамен
Программа.
#include <iostream.h>
#include <iomanip.h>
void main()
{
// зададим размерность массива с помощью констант
const int m = 3;
const int n = 2;
int A[m][n]; // объявление двумерного массива
// ====== ПЕРВЫЙ ЭТАП =========
// приглашение пользователю ввести данные в массив
cout << "Введите данные в массив. \n";
// построчный ввод данных в массив
for (int i=0; i<m; i++) // изменение индекса строки
for (int j=0; j<n; j++) // изменение индекса столбца
{
cout << "a[" << i << "][" << j << "] = ";
cin >> A[i][j];
}
cout << "\nИсходный массив: \n";
// выведем массив на экран в виде таблицы
for (i=0; i<m; i++) // изменение индекса строки
{
for (int j=0; j<n; j++) // изменение индекса столбца
cout << setw(5) << A[i][j] << " ";
cout << endl; // переход на следующую строку
}
// ====== ВТОРОЙ ЭТАП =========
cout << endl;
// поиск в строках максимального элемента
for (i=0; i<m; i++) // изменение индекса строки
{
int max = A[i][0]; /* предполагаем, что максимальный -
первый элемент строки */
// поиск максимального элемента в i-ой строке
for (int j=0; j<n; j++) // изменение индекса столбца
{
if (A[i][j] > max) max = A[i][j];
}
cout << "Максимальный элемент " << i
<< "-ой строки = " << max << endl;
}
// ====== ТРЕТИЙ ЭТАП =========
cout << endl;
// поиск в столбцах минимального элемента
for (i=0; i<n; i++) // изменение индекса !! столбца !!
{
int min = A[0][i];/* предполагаем, что мминимальный -
первый элемент столбца */
// поиск минимального элемента в i-ом столбце
for (int j=0; j<m; j++) // изменение индекса !! строки !!
{
if (A[j][i] < min) min = A[j][i];
}
cout << "Минимальный элемент " << i
<< "-ого столбца = " << min << endl;
}
}
Результаты работы программы.
Введите данные в массив.
a[0][0] = 12
a[0][1] = -2
a[1][0] = 5
a[1][1] = 23
a[2][0] = 567
a[2][1] = -342
Исходный массив:
12 -2
5 23
567 -342
Максимальный элемент 0-ой строки = 12
Максимальный элемент 1-ой строки = 23
Максимальный элемент 2-ой строки = 567
Минимальный элемент 0-ого столбца = 5
Минимальный элемент 1-ого столбца = -342
Многомерные динамические массивы
При решении на компьютере серьезных задач, например, при разработке приложений, интенсивно использующих ресурсы графики, всегда нужно иметь под рукой достаточное количество ресурсов GDI, которые лимитированы системой. Поэтому эффективные алгоритмы и способы управления динамической памятью часто приобретают решающее значение. Принцип организации динамического двухмерного массива, который часто используется в подобных случаях, проще всего уяснить с помощью следующей схемы:
Адрес массива |
Адреса массивов адресов |
Серия отдельных одномерных массивов |
а |
a[0] |
a[0][0] a[0][1] a[0][n-1] |
a[1] |
a[1][0] a[1][1] a[1][n-1] |
|
... |
... ... ... |
|
a[n-1] |
a[n-1][0] a[n-1][1] a[n-1][n-1] |
Отличие описанной схемы от схемы статического двухмерного массива состоит в том, что теперь для адресов a, a[0], a[1], … a[n - 1] должно быть отведено реальное физическое пространство памяти. В то время как для статического двухмерного массива выражение вида a, a[0], a[1], … a[n - 1] были всего лишь возможными конструкциями для ссылок на реально существующие элементы массива, но сами эти указатели не существовали как объекты в памяти компьютера. Алгоритм выделения памяти таков:
Определяем переменную a как адрес массива адресов:
float **a;
Выделяем область памяти для массива из n указателей на тип float и присваиваем адрес начала этой памяти указателю а. Оператор, выполняющий эти действия выглядит так:
a = new float* [n];
В цикле пробегаем по массиву адресов a[], присваивая каждому указателю a[i] адрес вновь выделяемой памяти под массив из n чисел типа float.
При работе с динамически задаваемыми массивами часто забывают освобождать память, захваченную для массива. Память следует вновь возвращать в распоряжение операционной системы, то есть освождать с помощью операции delete. Правда, при завершении работы функции main автоматически уничтожаются все переменные, созданные в программе, и указатели сегментов памяти получают свои исходные значения. Однако при разработке сложных многомодульных комплексов программ следует помнить о том, что выделенная память «повисает», становится недоступной операционной системе при выходе из области действия указателя, который ссылается на ее начало. Это может вызвать отказ в выделении новой памяти в каком-то другом программном модуле, если весь объем свободной области памяти будет исчерпан. Операция delete совместно с операцией new позволяет контролировать процесс последовательного выделения и высвобождения динамической памяти. Чтобы освободить память, выделенную для одной переменной d, например, с помощью оператора double *d = new double;, достаточно в конце функции или блока, где использовалась переменная d, записать delete d;. Если был размещен массив переменных, например float *p = new float[200], то в современных версиях компиляторов следует освобождать память оператором delete [] p;.
Здесь квадратные скобки указывают компилятору на то, что освобождать следует то количество ячеек, которое было захвачено в последней операции new в применении к указателю p. Явно указывать это число не нужно. Хотя компилятор Visual C++ 6.0 при попытке освободить память, занятую массивом, операцией delete без скобок не выдает сообщений об ошибке и, по-видимому, функционирует верно, однако для обеспечения надежности следует соблюдать условия стандарта. Заметим, что операция delete игнорирует нулевые указатели, поэтому проверка на неравенство нулю указателя перед тем, как освободить память, на которую он ссылается, является излишней.
Полезно будет поэкспериментировать с такими модулями:
// Динамический захват и освобождение памяти
double *a; // Одна переменная
double *d; // Массив переменных
double **dd; // Двухмерный массив
void GetMem()
{ // Захват памяти
a = new double; // Одна переменная
d = new double[4]; // Массив переменных
dd = new double*[3]; // Двухмерный массив
for(int i = 0; i< 3; i++)
dd[i] = new double[2];
// Присвоение
*a = 1.0; // Одна переменная
cout << "a = " << *a << endl;
cout << "Address of a = " << a << endl;
// Массив переменных
cout << "Array starts from " << d << " and has\n";
for(i = 0; i < 4; i++)
{
d[i] = double(i);
cout << "d[" << i << "] = " << d[i] << endl;
}
cout << "2D array starts from " << dd << " and has\n";
for(i = 0; i < 3; i++, cout << endl) // Двухмерный массив
for(int j = 0; j < 2; j++)
{
dd[i][j] = (double)(i + j);
cout << "dd[" << i << "][" << j << "] = " << dd[i][j] << endl;
}
}
void FreeMem()
{ // Освобождение памяти
delete a; // Одна переменная
delete [] d; // Массив переменных
for(int i = 0; i < 3; i++) // Двухмерный массив
delete [] dd[i];
delete [] dd;
}
Создайте функцию main, в которой поочередно вызывайте эти модули и анализируйте значения адресов. Попробуйте убрать скобки при операциях delete и вновь проверьте результат. При отладке этой программы следует быть очень аккуратными, так как здесь могут проявиться опасные особенности работы с указателями. Так, если в последнем цикле for по ошибке вместо i < 3 ввести, например, i < 5, то, возможно придется перегрузить компьютер с потерей незаписанного ввода. Поэтому следует особенно тщательно проверять границы массивов.
После освобождения памяти указатели f, d, и dd продолжают, тем не менее, указывать на те же адреса, что и до освобождения (однако эта память уже не наша). В этом легко можно убедиться, вставив до и после операций delete вывод:
cout << "a=" << a << ", d=" << d << ", dd=" << dd << ", dd[0]=" << dd[0];
Следовательно, необходимо аккуратно работать с указателями, которые адресуют освобожденную память и следить за обращением к памяти, ранее занимаемой объектом.