Опубликован: 25.08.2010 | Доступ: свободный | Студентов: 6102 / 599 | Оценка: 3.91 / 3.44 | Длительность: 11:09:00
Лекция 4:

Функции

< Лекция 3 || Лекция 4: 1234 || Лекция 5 >

Модульное программирование

С увеличением объема программы становится неудобно хранить ее в одном файле. Разбиение программы на функции является первым шагом в повышении уровня абстракции программы, следующий - группировка функций и связанных с ними данных в отдельные файлы (модули), компилируемые раздельно.

Получившиеся в результате компиляции объектные модули объединяются в исполняемую программу с помощью компоновщика. Разбиение на модули уменьшает время перекомпиляции и облегчает процесс отладки. Чем более независимы модули, тем легче отлаживать программу.

Модуль содержит данные и функции их обработки. Для того чтобы использовать модуль, нужно знать только его интерфейс. Интерфейсом модуля являются заголовки всех функций и описания доступных извне типов, переменных и констант. Описания глобальных программных объектов во всех модулях программы должны быть согласованы.

Модульность в языке С++ поддерживается с помощью директив препроцессора, пространств имен, классов памяти, исключений и раздельной компиляции.

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

Препроцессором называется первая фаза компилятора. Инструкции препроцессора называются директивами. Они должны начинаться с символа #, перед которым в строке могут находиться только пробельные символы.

Директива #include

Директива #include <имя_файла> вставляет содержимое указанного файла в ту точку исходного файла, где она записана. Включаемый файл также может содержать директивы #include.

Поиск файла, если не указан полный путь, ведется в стандартных каталогах включаемых файлов. Вместо угловых скобок могут использоваться кавычки (" ") - в этом случае поиск файла ведется в каталоге, содержащем исходный файл, а затем уже в стандартных каталогах.

Директива #include является простейшим средством обеспечения согласованности объявлений в различных файлах, включая в них информацию об интерфейсе из заголовочных файлов. Заголовочные файлы обычно имеют расширение .h и могут содержать:

  • определения типов, констант, встроенных функций, шаблонов, перечислений;
  • объявления функций, данных, имен, шаблонов;
  • пространства имен;
  • директивы препроцессора;
  • комментарии.

В заголовочном файле не должно быть определений функций и данных.

Директива #define

Директива #define определяет подстановку в тексте программы. Она используется для определения:

  • символических констант. Формат определения символической константы:
    /* Все вхождения имени заменяются на текст подстановки */
    #define имя текст_подстановки
  • макросов, которые выглядят как функции, но реализуются подстановкой их текста в текст программы. Формат определения макроса:
    #define имя( параметры ) текст_подстановки
  • символов, управляющих условной компиляцией. Они используются вместе с директивами #ifdef и #ifndef. Формат:
    #define имя

Примеры:

#define M 1000
#define Vasia "Василий Иванович"
#define MAX(a,b) ((x)>(y)?(x):(y))
#define __cplusplus

Параметры используются при макроподстановке, например, если в тексте программы используется вызов макроса y = MAX(sum1, sum2);, он будет заменен на

y=((sum1)>(sum2)?(sum1):(sum2));

Использования макросов и символических констант в программах следует избегать.

Области действия идентификаторов

Каждый программный объект имеет область действия, которая определяется видом и местом его объявления. Существуют следующие области действия: блок, файл, функция, прототип функции, класс и поименованная область.

Блок. Идентификаторы, описанные внутри блока, являются локальными. Область действия идентификатора начинается в точке определения и заканчивается в конце блока, видимость - в пределах блока и внутренних блоков, время жизни - локальное. После выхода из блока память освобождается.

Файл. Идентификаторы, описанные вне любого блока, функции, класса или пространства имен, имеют глобальную видимость и время жизни и могут использоваться с момента их определения.

Функция. Единственными идентификаторами, имеющими такую область действия, являются метки операторов. В одной функции все метки должны различаться, но могут совпадать с метками других функций.

Прототип функции. Идентификаторы, указанные в списке параметров прототипа (объявления) функции, имеют областью действия только прототип функции.

Класс. Элементы структур, объединений и классов (за исключением статических элементов) являются видимыми лишь в пределах класса. Они образуются при создании переменной указанного типа и разрушаются при ее уничтожении.

Поименованная область. С++ позволяет явным образом задать область определения имен как часть глобальной области с помощью оператора namespace.

Область видимости совпадает с областью действия за исключением ситуации, когда во вложенном блоке описана переменная с таким же именем. В этом случае внешняя переменная во вложенном блоке невидима, хотя он и входит в ее область действия. Тем не менее к этой переменной, если она глобальная, можно обратиться, используя операцию доступа к области видимости ::. Способ обратиться к скрытой локальной переменной отсутствует.

В каждой области действия различают так называемые пространства имен. Пространство имен - область, в пределах которой идентификатор должен быть уникальным. В разных пространствах имена могут совпадать, поскольку разрешение ссылок осуществляется по контексту идентификатора в программе, например:

struct Node
{
int Node;
int i;
}Node;

В данном случае противоречия нет, поскольку имена типа, переменной и элемента структуры относятся к разным пространствам. В С++ определено четыре раздельных класса идентификаторов.

  • К одному пространству имен относятся имена переменных, функций, типов, определенных пользователем ( typedef ) и констант перечислений в пределах одной области видимости. Все они, кроме имен функций, могут быть переопределены во вложенных блоках.
  • Другой класс имен образуют имена типов перечислений, структур, классов и объединений. Каждое имя должно отличаться от имен других типов в той же области видимости.
  • Отдельный класс составляют элементы каждой структуры, класса и объединения. Имя элемента должно быть уникально внутри структуры, но может совпадать с именами элементов других структур.
  • Метки образуют отдельное пространство имен.
Внешние объявления

Любая функция автоматически видна во всех модулях программы. Если требуется ограничить область действия функции файлом, в котором она описана, используется модификатор static.

Для того чтобы сделать доступной в нескольких модулях переменную или константу, необходимо:

  • определить ее ровно в одном модуле как глобальную;
  • в других модулях объявить ее как внешнюю с помощью модификатора extern.

Другой способ - поместить это объявление в заголовочный файл и включить его в нужные модули1Напомню, что объявление, в отличие от определения, не создает переменную. Объявление с extern не должно содержать инициализацию: если она присутствует, модификатор extern игнорируется .

Все описания одной и той же переменной должны быть согласованы.

Пример описания двух глобальных переменных в файлах one.cpp и two.cpp с помощью заголовочного файла my_header.h:

// my_header.h - внешние объявления
extern int a; 
extern double b; 
...
// one.cpp
#include "my_header.h"
int a;
...
// two.cpp
#include "my_header.h"
double b;
...

Если переменная описана как static, область ее действия ограничивается файлом, в котором она описана.

Поименованные области

Поименованные области служат для логического группирования объявлений. Чем больше программа, тем более актуально использование поименованных областей. Простейшим примером применения является отделение кода, написанного одним человеком, от кода, написанного другим.

Объявление поименованной области (ее также называют пространством имен) имеет формат:

namespace [ имя_области ]{ /* Объявления */}

Поименованная область может объявляться неоднократно, причем последующие объявления рассматриваются как расширения предыдущих.

Если имя области не задано, компилятор определяет его самостоятельно с помощью уникального идентификатора, различного для каждого модуля. Объявление объекта в непоименованной области равнозначно его описанию как глобального с модификатором static.

Нельзя получить доступ из одного файла к элементу неименованной области другого файла.

Пример.

namespace demo{
	int i = 1;
	int k = 0;
	void func1(int);
	void func2(int) { /* ... */}
}
namespace demo{			// Расширение
	// int i = 2;	       Неверно - двойное определение
	void func1(double);	// Перегрузка
	void func2(int);		// Верно (повторное объявление)
}

В объявлении поименованной области могут присутствовать как объявления, так и определения. Логично помещать в нее только объявления, а определять их позднее с помощью имени области и оператора доступа к области видимости ::, например:

void demo::func1(int) { /* ... */}

Это применяется для разделения интерфейса и реализации. Таким способом нельзя объявить новый элемент пространства имен.

Объекты, объявленные внутри области, являются видимыми с момента объявления. К ним можно явно обращаться с помощью имени области и оператора доступа к области видимости ::, например:

demo::i = 100; demo::func2(10);

Если имя часто используется вне своего пространства, можно объявить его доступным с помощью оператора using:

using demo::i;

После этого можно использовать имя без явного указания области.

Если требуется сделать доступными все имена из какой-либо области, используется оператор using namespace:

using namespace demo;

Операторы using и using namespace можно использовать и внутри объявления поименованной области, чтобы сделать в ней доступными объявления из другой области.

Пространства имен стандартной библиотеки

Объекты стандартной библиотеки определены в пространстве имен std. Например, объявления стандартных средств ввода/вывода С в заголовочном файле <stdio.h> помещены в пространство имен следующим образом:

// stdio.h
namespace std
{
int printf(const char**);
... 
}
using namespace std;

Это обеспечивает совместимость сверху вниз. Для тех, кто не желает присутствия неявно доступных имен, определен новый заголовочный файл <cstdio>:

// cstdio
namespace std{
int printf(const char**);
... 
}

Если в программу включен файл <cstdio>, нужно указывать имя пространства имен явным образом:

std::printf("*");

Механизм пространств имен вместе с директивой #include обеспечивают необходимую при написании больших программ гибкость путем сочетания логического группирования связанных величин и ограничения доступа к ненужным средствам.

< Лекция 3 || Лекция 4: 1234 || Лекция 5 >
Dana Kanatkyzi
Dana Kanatkyzi
Здравствуйте.Помогите решить задачу минимум 4 чисел.Условие такое:"Напишите функцию int min (int a, int b, int c, int d) (C/C++)"находящую наименьшее из четырех данных чисел."Заранее спасибо!
Юрий Семевский
Юрий Семевский
Россия, Санкт-Петербург
Александр Богдашов
Александр Богдашов
Россия