Тема:
«Модульное программирование»
3. Особенности использования глобальных переменных
Модульное программирование – это процесс разработки программы, состоящей из
нескольких логически завершенных и взаимосвязанных единиц – модулей.
Модуль – файл программы, объединяющий некоторое
количество функций, определенных типов данных и переменных.
Преимущества использования модулей:
1. Разбиение полного текста программы на несколько файлов.
2. Использование как заготовки для других программ.
Модульность в языке C++ поддерживается с помощью директив препроцессора,
пространств имен, классов памяти, исключений и раздельной компиляции (строго
говоря, раздельная компиляция не является элементом языка, а относится к его
реализации). (начало)
Препроцессор лучше
всего рассматривать как отдельную программу, которая выполняется перед
компиляцией. При запуске программы препроцессор просматривает код сверху вниз,
файл за файлом, в поиске директив.
Директивы – это
специальные команды, которые начинаются с символа # и НЕ заканчиваются
точкой с запятой. Есть несколько типов директив, которые мы рассмотрим ниже.
Заголовочные файлы включаются в текст программы с
помощью директивы препроцессора #include. Директивы препроцессора начинаются со знака
"диез" (#), который должен быть самым первым
символом строки. Программа, которая обрабатывает эти директивы, называется препроцессором (в современных
компиляторах препроцессор обычно является частью самого компилятора).
Директива #include включает в программу содержимое указанного файла. Имя файла может быть
указано двумя способами:
#include
<some_file.h>
#include
"my_file.h"
#include
<some_file.h> сообщает препроцессору искать некий стандартный заголовочный файл в
системных путях. Компилятор ищет этот файл в предопределенных местах. (Способ
определения этих мест сильно различается для разных платформ и реализаций).
#include
"my_file.h" означает, что заголовочный файл – пользовательский, и его поиск начинается
с того каталога, где находится исходный текст программы.
Заголовочный файл также может содержать директивы #include. Поэтому иногда трудно понять, какие же конкретно
заголовочные файлы включены в данный исходный текст, и некоторые заголовочные
файлы могут оказаться включенными несколько раз. Избежать этого позволяют условные директивы препроцессора.
Рассмотрим пример:
#ifndef
MYFILE_H //если не объявлена константа MYFILE_H
#define
MYFILE_H //объявляем
её и продолжаем обрабатывать заголовочный файл до конца блока проверки
/* содержимое файла myfile.h */
#endif //конец
блока проверки
Условная директива #ifndef (англ. «if
not defined» = «если не определено»)
проверяет, не было ли значение MYFILE_H определено ранее. (MYFILE_H – это константа препроцессора; такие константы принято
писать заглавными буквами.) Препроцессор обрабатывает следующие строки вплоть
до директивы #endif.
В противном случае он пропускает строки от
#ifndef до #endif.
Директива
#define
MYFILE_H
определяет константу препроцессора MYFILE_H. Поместив эту директиву непосредственно после
директивы #ifndef, мы можем
гарантировать, что содержательная часть заголовочного файла myfile.h
будет включена в исходный текст только один раз, сколько бы раз ни включался в
текст сам этот файл.
Пример. Пусть имеется программа вычисления факториала
числа.
//подключение
заголовочного файла
#include <iostream.h>
//прототип
функции вычисления факториала
long int
fact(int n);
//главная программа
int main()
{
system("chcp 1251>nul");
int n;
cout<<"Введите число: ";
cin>>n;
//вывод
результата вычисления факториала
cout<<n<<"!="<<fact(n)<<endl;
system("pause");
//завершение
работы программы
return 0;
}
//реализация функции вычисления факториала
long int
fact(int n)
{
//начальное
значение для значения факториала
long int rez=1;
//цикл подсчета произведения
for(int i=1; i<=n;
i++)
rez*=i;
//вернуть результат в главную программу
return rez;
}
Выносим функцию для вычисления факториала в отдельный модуль mod1.h
//если не
объявлена константа MOD1_H
#ifndef MOD1_H
//объявляем ее
#define
MOD1_H
//прототип
функции вычисления факториала
long int fact(int n);
//реализация функции вычисления факториала
long int
fact(int n)
{
//начальное
значение для значения факториала
long int rez=1;
//цикл подсчета произведения
for(int i=1; i<=n;
i++)
rez*=i;
//вернуть результат в главную программу
return rez;
}
//конец блока проверки
#endif
//подключение
заголовочного файла
#include <iostream.h>
//подключение
пользовательского заголовочного файла
#include "mod1.h"
//главная
программа
int main()
{
system("chcp 1251>nul");
int n;
cout<<"Введите число: ";
cin>>n;
//вывод
результата вычисления факториала
cout<<n<<"!="<<fact(n)<<endl;
system("pause");
//завершение
работы программы
return 0;
}
Пример. Дан одномерный массив. Реализовать следующее:
· ввод элементов массива;
· вывод элементов массива на печать;
· вычисление суммы элементов массива.
Создать главную программу main.cpp. В программе подключить три заголовочных файла с именами vvod.h, vivod.h и summ.h. В первом файле описать функцию заполнения элементов одномерного массива, во втором – функцию вывода элементов массива, в третьем – функцию расчета суммы элементов.
Модуль vvod.h для заполнения массива будет иметь вид:
//если
константа MOD1_H не объявлена
#ifndef MOD1_H
//объявляем
константу MOD1_H
#define MOD1_H
//прототип функции заполнения массива
void formmas(float *m,int n);
//реализация функции заполнения массива
void formmas (float *m, int n)
{
//локальные
переменные для диапазона формирования массива
float a,b;
//локальная
переменная номер элемента в массиве
int i;
cout<<"Введите диапазон (a, b):
";
cin>>a>>b;
//цикл
заполнения массива случайными числами
for (i=0;i<=n-1;i++)
m[i]=(b-a)*rand()/32767+a;
//завершение
подпрограммы
return;
}
//конец блока проверки
#endif
Модуль vivod.h для вывода элементов массива на печать будет иметь вид:
//если
константа MOD2_H не объявлена
#ifndef MOD2_H
//объявляем
константу MOD2_H
#define MOD2_H
//прототип функции вывода элементов массива
на экран
void vivodmas(float *m,int n);
//реализация функции вывода элементов
массива на экран
void vivodmas (float *m, int n)
{
//локальная
переменная номер элемента в массиве
int i;
//указываем
число цифр в выводимом числе – элементе массива
cout.precision(3);
//цикл
вывода элементов на экран
for (i=0;i<=n-1;i++)
cout<<m[i]<<" ";
cout<<"\n";
return;
}
//конец блока проверки
#endif
Модуль summ.h для вычисления суммы элементов массива будет иметь вид:
//если константа
MOD3_H не объявлена
#ifndef MOD3_H
//объявляем
константу MOD3_H
#define MOD3_H
//прототип функции
расчета суммы элементов массива
float
summa(float *m,int n);
//реализация функции
расчета суммы элементов массива
float summa
(float *m, int n)
{
//локальная
переменная номер элемента в массиве
int i;
//локальная
переменная сумма элементов массива
float sum=0;
//цикл расчета суммы
for (i=0;i<=n-1;i++)
sum+=m[i];
//вернуть
значение суммы в главную программу
return sum;
}
//конец блока проверки
#endif
Главная программа main.cpp содержит следующий текст:
//подключение заголовочных файлов
#include <iostream.h>
//подключение созданных файлов из текущей
папки
#include
"mod1.h"
#include
"mod2.h"
#include
"mod3.h"
//главная программа
int main()
{
system("chcp 1251>nul");
//переменная количество элементов массива
int n;
//одномерный
динамический массив вещественных чисел
float *mas;
cout<<"Введите количество элементов массива n:
";
cin>>n;
//выделяем память под n элементов
массива
mas = new
float[n];
//вызываем функцию заполнения массива
formmas(mas,n);
//вызываем функцию вывода элементов массива на экран
vivodmas(mas,n);
//выводим результат вычисления суммы массива
cout<<"Сумма элементов массива =
"<<summa(mas,n)<<endl;
//пауза для просмотра результата
system("pause");
//завершение
работы программы
return 0;
}
(начало)
Если можно отказаться от использования глобальных
переменных, то лучше это сделать. Желательно стремиться к тому, чтобы любой
файл проекта «не лез к соседу за данными, а сосед не разбрасывал эти данные в
виде глобальных переменных». Обмен данными между функциями должен осуществлять
за счет передачи данных в качестве параметров и возврата значений с помощью
оператора return. (Массивов это не
касается.)
Однако в языке программирования C++ есть
проблема. С помощью return можно вернуть
только одно значение. Но могут быть случаи, когда функция должна изменить
несколько переменных (здесь не имеются ввиду элементы массива). В таком случае без глобальных переменных
обойтись сложно.
·
Если в файле a.cpp объявлена переменная за пределами любой функции
(например, так: int count), то она
является глобальной для всех файлов проекта. Чтобы получить значение этой
переменной в файле aa.cpp
достаточно просто указать ее имя (если в функции нет локальной переменной с тем
же именем). Чтобы получить значение из других файлов, надо указать, что имеется
в виду глобальная переменная, а не локальная. Делается это с помощью ключевого
слова extern (например, extern count).
·
Бывают
ситуации, когда в одном файле для нескольких содержащихся в нем функций нужна
глобальная переменная. Но эта переменная не должна быть доступна функциям,
содержащимся в других файлах. В таком случае глобальная переменная объявляется
с ключевым словом static (например, static int count). Тем самым мы как бы скрываем глобальную
переменную.
Задание. Напишите простые наглядные примеры, использования глобальных функций:
1. Объявите глобальную переменную в одном файле, а
получите ее значение в другом файле (выведите на экран).
2. Объявите в одном файле статическую глобальную
переменную. Выведите ее значение на экран из функции данного файла. Попытайтесь
сделать это из функции другого файла.
3. Создайте две глобальные переменные в одном файле.
В другом файле напишите функцию, которая меняет их значение.
(начало)