Р. ЛАФОРЕ ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ В C++ (4-Е ИЗДАНИЕ) (часть13) онлайн

Пространства имен

Мы уже знаем, как ограничить видимость элементов программ, объявляя их

внутри файла или класса либо делая их static или const. Иногда, тем не менее,

требуется более гибкий подход к этому вопросу.

Например, при написании библиотеки классов программисты предпочитают

использовать короткие и понятные имена для функций, не являющихся метода-

ми, и отдельных классов. Например, add(), book. А эти имена уже могут быть

выбраны другими разработчиками классов в их библиотеках или программиста-

ми, использующими в своих программах данную библиотеку. Это может при-

вести к «столкновению имен», и вы долго будете ломать голову над тем, откуда

берется сообщение компилятора о повторном определении. Перед введением

понятия пространства имен программистам приходилось использовать длинные

имена во избежание этой коллизии:

 

Sancho's_Simplified_Statistics_Library_Forever_add();

 

He нужно объяснять, что использование подобных имен переменных, функ-

ций, классов сильно усложняет восприятие листингов, усложняет работу про-

граммистов, а сами листинги неоправданно раздуваются в объеме. Пространства

имен решают эту проблему (надо отметить, что обычные методы классов не

могут вызывать столкновения имен, так как их видимость ограничена классом).

Определение пространства имен

Пространство имен — это некая именованная область файла. Следующий код

определяет пространство имен geo с некоторыми объявлениями внутри него.

namespace geo

{

                const double PI = 3.14159;

                double circumf(double radius)

                { return 2 * PI * radius; }

}   //конец пространства

 

Фигурные скобки ограничивают пространство имен. Переменные и другие

элементы программы, объявленные между ними, называются членами простран-

ства имен. Обратите внимание: за закрывающей фигурной скобкой не следует

точка с запятой, как в классах.

Доступ к членам пространств имен

Часть кода, находящаяся вне пространства имен, не имеет доступа к его чле-

нам, по крайней мере, обычным способом. Пространство имен делает их неви-

димыми:

namespace geo

{

                const double PI = 3.14159;

                double circumf(double radius)

                { return 2 * PI * radius; }

}   //конец пространства geo

double c = circumf(10); //здесь это не сработает

Чтобы иметь доступ к элементам пространства имен извне, необходимо при

обращении к ним использовать название этого пространства. Это можно сде-

лать двумя способами. Во-первых, каждый элемент можно предварять названи-

ем пространства и оператором разрешения контекста:

double c = geo::circumf(10);  //полный порядок

Во-вторых, можно использовать директиву using:

 

using namespace geo;

double c = circumf(10);        //полный порядок

 

Директива using обычно делает пространство имен видимым начиная с места

ее написания и до конца. Можно сузить область действия директивы до блока,

используя ее, например, в функции:

void seriousCalcs()

{

                using namespace geo;

                //идет какой-то код

                double с = circumf(10); //OK

}

double с = circumf(10); //Не работает

Как видим, здесь члены пространства имен видны только внутри тела функции.

Пространства имен в заголовочных файлах

Пространства имен часто используются в заголовочных файлах библиотек клас-

сов или функций. Каждая библиотека может иметь свое пространство имен.

На данный момент вы уже знакомы с пространством std, чьи члены составляют

Стандартную библиотеку C++.

Неоднократное определение пространств имен

Определения пространств имен могут встречаться в тексте программы несколь-

ко раз:

 

namespace geo

{

                const double PI = 3.14159;

} //конец пространства geo

//тут какой-то код

namespace geo

{

                double circumf(double radius)

                { return 2 * PI * radius; }

} //конец пространства geo

 

Это выглядит как двойное определение пространства, но на самом деле это

просто продолжение того же определения. Эта маленькая хитрость позволяет

использовать одно и то же пространство имен в нескольких заголовочных фай-

лах. В Стандартной библиотеке C++ примерно дюжина заголовочных файлов

использует пространство std.

//fileA.h

namespace alpha

{

                void funcA();

}

//fileB.h

namespace alpha

{

                void funcB();

}

//файл Main.cpp

#include "fileA.h"

#include "fileB.h"

using namespace alpha;

funcA();

funcB();

 

Объявления можно помещать и вне пространства имен, и они будут рабо-

тать, как если бы они были внутри него. Все, что для этого нужно, это имя про-

странства и оператор разрешения контекста:

namespace beta

{

                int uno;

}

int beta::dos;

В этом примере и uno, и dos являются объявлениями в пространстве имен

beta.

Неименованные пространства имен

Можно создать пространство имен и не присваивая ему имени. При этом автома-

тически создастся пространство, элементы которого видны только из данного

файла. Компилятор дает ему имя, совпадающее с именем файла. Члены неиме-

нованного пространства имен видны отовсюду внутри файла. В следующем лис-

 

тинге функции funcA() и funcB() имеют доступ к переменной gloVar, каждая из

своего файла.

//fileA.cpp

namespace        //неименованное пространство, для fileA.cpp

{

                int gloVar = 111;

}

funcA()

{cout << gloVar; } //Выводит: 111

 

//fileB.cpp

namespace        //неименованное пространство, для fileB.cpp

{

                int gloVar = 222;

}

funcB()

{cout << gloVar; } //Выводит: 222

 

В данном примере оба файла содержат переменную gloVar, но конфликта

имен не возникает, так как переменные объявлены в неименованных простран-

ствах имен и находятся в разных файлах.

Это как бы альтернатива использованию static для уменьшения зоны види-

мости глобальных переменных до размеров одного файла. На самом деле ис-

пользование пространств имен сейчас признается более предпочтительным, не-

жели использование static-элементов.

 

Переименование типов с помощью typedef

Зарезервированное слово typedef может оказаться полезным в определенных

ситуациях, и вы наверняка встречали его, по крайней мере, в чужих листингах.

С его помощью можно создать новое имя для типа данных. Например, выра-

жение

typedef unsigned long unlong;

 

переименовывает unsigned long в unlong, точнее, делает их синонимами. Теперь

можно объявлять переменные с использованием нового имени:

 

unlong var1, var2;

 

Это может помочь сэкономить немножко места. С большей пользой можно

применить это переименование для того, чтобы расширить назначение перемен-

ных этого типа:

typedef int FLAG;           //переменные типа int для хранения флагов

typedef int KILOGRAMS; //переменные типа int для килограммов

Если вам не нравится, как в C++ задаются указатели, можно изменить их

объявление:

int *p1, *p2, *p3;     //обычное объявление

typedef int* ptrInt;   //новое имя для указателя на int

ptrInt p1, p2, p3;     //упрощенное объявление

Вот так. Чтобы не писать эти надоедливые звездочки.

 

Так как классы являются типами в C++, можно использовать typedef для их

переименования. Ранее мы говорили, что иногда приходится называть данные

длинными именами. Писать их всякий раз — утомительно и долго. Но, не пере-

именовывая сам класс, можно создать синоним его имени, так сказать, задать

уменьшительно-ласкательное его название с помощью typedef:

class GeorgeSmith_Display_Utility         //определение класса

{

                //члены класса

};

typedef GeorgeSmith_Display_Utility GSdu; //новое имя класса

GSdu anObj;                           //создать Объект, используя новое имя

Переименование обычно используется в заголовочных файлах для того, что-

бы новые имена могли использоваться множеством исходных файлов. Многие

разработчики ПО настолько любят typedef, что в результате их переименований

получается практически другой язык программирования.

Теперь, после изучения некоторых теоретических положений, касающихся

многофайловых программ, можно перейти к практическим примерам. В них, ко-

нечно, не охвачены все темы, которые мы рассматривали в этой главе, но они

показывают общую концепцию работы программиста с программами, состоящи-

ми из нескольких файлов.

 

13