Главная страница

Язык программирования C Пятое издание


Скачать 1.85 Mb.
НазваниеЯзык программирования C Пятое издание
Дата15.07.2019
Размер1.85 Mb.
Формат файлаpdf
Имя файла620354-www.libfox.ru.pdf
ТипДокументы
#84130
страница53 из 54
1   ...   46   47   48   49   50   51   52   53   54
Упражнение 18.11. Почему так важно, чтобы функция what() не передавала исключений?
18.2. Пространства имен
В больших программах обычно используют библиотеки от независимых разработчиков. В
таких библиотеках обычно определено множество глобальных имен классов, функций и шаблонов. Когда приложение использует библиотеки от многих разных поставщиков,
некоторые из этих имен почти неизбежно совпадут. Библиотеки, помещающие имена в глобальное пространство имен, вызывают загромождение пространства имен (namespace pollution).
Традиционно программисты избегают загромождения пространства имен, используя для глобальных сущностей очень длинные имена, зачастую содержащие префикс, означающий библиотеку, в которой определено имя:
Page 979/1103
class cplusplus_primer_Query { ... }; string cplusplus_primer_make_plural(size_t, string&);
Это решение далеко от идеала: программистам неудобно писать и читать программы,
использующие длинные имена.
Пространства имен (namespace) предоставляют намного более контролируемый механизм предотвращения конфликтов имени. Пространства имен разделяют глобальное пространство имен. Пространство имен — это область видимости. При определении имен библиотеки в пространстве имен, авторы (и пользователи) библиотеки могут избежать ограничений,
присущих глобальным именам.
18.2.1. Определение пространств имен
Определение пространства имен начинается с ключевого слова namespace,
сопровождаемого именем пространства имен. После имени пространства имен следуют заключенные в фигурные скобки объявления и определения. В пространство имен может быть помещено любое объявление, которое способно присутствовать в глобальной области видимости, включая классы, переменные (с инициализацией), функции (с их определениями),
шаблоны и другие пространства имен. namespace cplusplus_primer { class Sales_data { /* ... */};
Sales_data operator+(const Sales_data&, const Sales_data&); class Query { /* ... */ }; class Query_base { /* ... */};
} // подобно блокам, пространства имен не завершаются точкой с запятой
Этот код определяет пространство имен cplusplus_primer с четырьмя членами: тремя классами и перегруженным оператором +.
Подобно другим именам, имя пространства имен должно быть уникальным в той области видимости, в которой оно определено. Пространства имен могут быть определены в глобальной области видимости или в другом пространстве имен. Они не могут быть определены в функциях или классах.
Область видимости пространства имен не заканчивается точкой с запятой. Каждое пространство имен является областью видимости
Как и в случае любой области видимости, каждое имя в пространстве имен должно относиться к уникальной сущности в пределах данного пространства имен. Поскольку разные пространства имен вводят разные области видимости, в разных пространствах имен могут быть члены с одинаковым именем.
Page 980/1103

К именам, определенным в пространстве имен, другие члены данного пространства имен могут обращаться непосредственно, включая области видимости, вложенные в пределах этих членов. Код вне пространства имен должен указывать пространство имен, в котором определено имя: cplusplus_primer::Query q = cplusplus_primer::Query("hello");
Если другое пространство имен (например, AddisonWesley) тоже содержит класс Query и этот класс необходимо использовать вместо определенного в пространстве имен cplusplus_primer,
приведенный выше код придется изменить следующим образом:
AddisonWesley::Query q = AddisonWesley::Query("hello"); Пространства имен могут быть разобщены
Как упоминалось в разделе 16.5, в отличие от других областей видимости, пространство имен может быть определено в нескольких частях. Вот определение пространства имен: namespace nsp {
// объявления
}
Этот код определяет новое пространство имен nsp или добавляет члены к уже существующему. Если пространство имен nsp еще не определенно, то создается новое пространство имен с этим именем. В противном случае это определение открывает уже существующее пространство имен и добавляет в него новые объявления.
Тот факт, что определения пространств имен могут быть разобщены, позволяет составить пространство имен из отдельных файлов интерфейса и реализации. Таким образом,
пространство имен может быть организовано таким же образом, как и определения собственных классов или функций.
• Члены пространства имен, являющиеся определениями классов, объявлениями функций и объектов, составляющих часть интерфейса класса, могут быть помещены в файлы заголовка.
Эти заголовки могут быть подключены в те файлы, которые используют эти члены пространства имен.
• Определения членов пространства имен могут быть помещены в отдельные файлы исходного кода.
Организовав пространство имен таким образом, можно также удовлетворить требование,
согласно которому различные сущности, включая не подлежащие встраиванию функции,
статические переменные-члены, переменные и т.д., должны быть определены в программе только один раз. Это требование распространяется и на имена, определенные в пространстве имен. Отделив интерфейс и реализацию, можно гарантировать, что имена функций и другие имена будут определены только один раз и именно это объявление будет многократно использоваться впоследствии.
Для представления несвязанных типов в составных пространствах имен следует использовать отдельные файлы. Определение пространства имен cplusplus_primer
Используя эту стратегию для отделения интерфейса от реализации, определим библиотеку
Page 981/1103
cplusplus_primer в нескольких отдельных файлах. Объявления класса Sales_data и связанных с ним функций поместим в файл заголовка Sales_data.h, а таковые для класса Query (см.
главу 15) — в заголовок Query.h и т.д. Соответствующие файлы реализации были бы в таких файлах, как Sales_data.cc и Query.cc:
//
---- Sales_data.h ----
// директивы #include должны быть перед открытием пространства имен
#include <string> namespace cplusplus_primer { class Sales_data { /* ... */};
Sales_data operator+(const Sales_data&, const Sales_data&);
// объявления остальных функций интерфейса класса Sales_data
}
//
---- Sales_data.cc ----
// все директивы #include перед открытием пространства имен
#include "Sales_data.h" namespace cplusplus_primer {
// определения членов класса Sales_data и перегруженных операторов
}
Использующая эту библиотеку программа включила бы все необходимые заголовки. Имена в этих заголовках определены в пространстве имен cplusplus_primer:
//
---- user.cc ----
// имена заголовка Sales_data.h находятся в пространстве
// имен cplusplus_primer
Page 982/1103

#include "Sales_data.h" int main() { using cplusplus_primer::Sales_data;
Sales_data trans1, trans2;
// ... return 0;
}
Подобная организация программы придает библиотеке свойство модульности, необходимое как разработчикам, так и пользователям. Каждый класс организован в виде двух файлов:
интерфейса и реализации. Пользователь одного класса вовсе не должен использовать при компиляции другие классы. Их реализацию можно скрыть от пользователей, разрешив при этом компилировать и компоновать файлы Sales_data.cc и user.cc в одну программу, причем без опасений по поводу возникновения ошибок во время компиляции или компоновки. Кроме того, разработчики библиотеки могут работать над реализацией каждого класса независимо.
В использующую эту библиотеку программу следует подключить все необходимые заголовки.
Имена в этих заголовках определены в пространстве имен cplusplus_primer.
Следует заметить, что директивы #include обычно не помещают в пространство имен. Если попробовать сделать это, то произойдет попытка определения всех имен в этом заголовке как членов окружающего пространства имен. Например, если бы файл Sales_data.h открыл пространство имен cplusplus_primer прежде, чем включить заголовок string, то в программе была бы ошибка, поскольку это привело бы к попытке определить пространство имен std в пространстве имен cplusplus_primer. Определение членов пространства имен
Если объявления находятся в области видимости, то код в пространстве имен может использовать короткую форму имен, определенных в том же (или вложенном) пространстве имен:
#include "Sales_data.h" namespace cplusplus_primer { // повторное открытие cplusplus_primer
// члены, определенные в пространстве имен, могут использовать имена
// без уточнений std::istream& operator>>(std::istream& in, Sales_data& s) { /* ... */}
}
Член пространства имен может быть также определен вне определения пространства имен.
Для этого применяется подход, подобный определению членов класса вне его. Объявление
Page 983/1103
пространства имен должно находиться в области видимости, а в определении следует указать пространство имен, которому принадлежит имя.
// члены пространства имен, определенные вне его, должны использовать
// полностью квалифицированные имена cplusplus_primer::Sales_data cplusplus_primer::operator+(const Sales_data& lhs, const Sales_data& rhs) {
Sales_data ret(lhs);
// ...
}
Подобно членам класса, определенным вне самого класса, когда встречается полностью определенное имя, оно находится в пределах пространства имен. В пространстве имен cplusplus_primer можно использовать другие имена членов пространства имен без квалификации. Таким образом, хотя класс Sales_data является членом пространства имен cplusplus_primer, для определения параметров его функций можно использовать его имя без квалификации.
Хотя член класса пространства имен может быть определен вне его определения, такие определения должны присутствовать в окружающем пространстве имен. Таким образом,
оператор operator+ класса Sales_data можно определить в пространстве имен cplusplus_primer или в глобальной области видимости. Но он не может быть определен в несвязанном пространстве имен. Специализация шаблона
Специализация шаблона должна быть определена в том же пространстве имен, которое содержит первоначальный шаблон (см. раздел 16.5). Подобно любым другим именам пространства имен, пока специализация объявлена в пространстве имен, ее можно определить вне пространства имен:
// специализацию нужно объявить как член пространства std namespace std { template <> struct hash<Sales_data>;
}
// добавив объявление для специализации к пространству std,
// специализацию можно определить вне пространства имен std
Page 984/1103
template <> struct std::hash<Sales_data> { size_t operator()(const Sales_data& s) const { return hash<string>()(s.bookNo) ^ hash<unsigned>()(s.units_sold) ^ hash<double>()(s.revenue);
}
// другие члены как прежде
}; Глобальное пространство имен
Имена, определенные в глобальной области видимости (т.е. имена, объявленные вне любого класса, функции или пространства имен), определяются в глобальном пространстве имен (global namespace). Глобальное пространство имен неявно объявляется и существует в каждом приложении. Каждый файл, который определяет сущность в глобальной области видимости (неявно), добавляет ее имя к глобальному пространству имен.
Для обращения к членам глобального пространства имен применяется оператор области видимости (оператор ::) (scope operator). Поскольку глобальное пространство имен неявно, у него нет имени.
Форма записи при обращении к члену глобального пространства имен имеет следующий вид.
:: член_имя Вложенные пространства имен
Вложенное пространство имен (nested namespace) — это пространство имен, определенное в другом пространстве имен: namespace cplusplus_primer {
// первое вложенное пространство имен: определение части
// библиотеки Query namespace QueryLib { class Query { /* ... */ };
Query operator&(const Query&, const Query&);
// ...
}
Page 985/1103

// второе вложенное пространство имен: определение части
// библиотеки Sales_data namespace Bookstore { class Quote { /* ... */ }; class Disc_quote : public Quote { /* ... */ };
// ...
}
}
Вложенное пространство имен — это вложенная область видимости, ее область видимости вкладывается в пределы содержащего ее пространства имен. Имена вложенных пространств имен подчиняются обычным правилам: имена, объявленные во внутреннем пространстве имен, скрывают объявления того же имени во внешнем пространстве. Имена, определенные во вложенном пространстве имен, являются локальными для внутреннего пространства имен.
Код во внешних частях окружающего пространства имен может обратиться к имени во вложенном пространстве имен только через его квалифицированное имя. Например, имя класса QueryLib, объявленного во вложенном пространстве имен, выглядит следующим образом: cplusplus_primer::QueryLib::Query Встраиваемые пространства имен
Новый стандарт ввел новый вид вложенного пространства имен — встраиваемое пространство имен (inline namespace). В отличие от обычных вложенных пространств имен, имена из встраиваемого пространства имен применяются так, как будто они являются непосредственными членами окружающего пространства имен. Таким образом,
нет необходимости в квалификации имен из встраиваемого пространства имен. Для доступа к ним достаточно использовать имя окружающего пространства имен.
Для определения встраиваемого пространства имен ключевое слово namespace предваряется ключевым словом inline: inline namespace FifthEd {
// пространство имен для кода Primer Fifth Edition
} namespace FifthEd { // неявно встраиваемая class Query_base { /* ... */};
//
Page 986/1103
другие объявления, связанные с классом Query
}
Это ключевое слово должно присутствовать в первом определении пространства имен. Если пространство имен вновь открывается позже, ключевое слово inline не обязательно, но может быть повторено.
Встраиваемые пространства имен зачастую используются при изменении кода от одного выпуска приложения к следующему. Например, весь код текущего издания
Вводного курса можно поместить во встраиваемое пространство имен. Код предыдущих версий был бы в обычных, а не встраиваемых пространствах имен: namespace FourthEd { class Item_base { /* ... */}; class Query_base { /* ... */};
// другой код из Fourth Edition
}
Общее пространство имен cplusplus_primer включило бы определения обоих пространств имен. Например, с учетом того, что каждое пространство имен было определено в заголовке с соответствующим именем, пространство имен cplusplus_primer можно определить следующим образом: namespace cplusplus_primer {
#include "FifthEd.h"
#include "FourthEd.h"
}
Поскольку пространство имен FifthEd встраиваемое, код обращающийся к имени из пространства имен cplusplus_primer::, получит версию из этого пространства имен. Если понадобится код прежнего издания, к нему можно обратиться как к любому другому вложенному пространству имен, указав все имена окружающих пространств имен, например:
cplusplus_primer::FourthEd::Query_base. Безымянные пространства имен
У безымянного пространства имен (unnamed namespace) сразу за ключевым словом namespace следует блок объявлений, разграниченных фигурными скобками. У переменных,
определенных в безымянном пространстве имен, статическая продолжительность существования: они создаются перед их первым использованием и удаляются по завершении программы.
Безымянное пространство имен может быть разобщено в пределах данного файла, но не охватывающих файлов. У каждого файла есть собственное безымянное пространство имен.
Если два файла содержат безымянные пространства имен, эти пространства имен не связаны. Оба безымянных пространства имен могут определить одинаковое имя, и эти определения будут относиться к разным сущностям. Если заголовок определяет безымянное
Page 987/1103
пространство имен, то имена в этом пространстве определяют сущности, локальные для каждого файла, включенного в заголовок.
В отличие от других пространств имен, безымянное пространство является локальным для конкретного файла и никогда не охватывает несколько файлов.
Имена, определенные в безымянном пространстве имен, используются непосредственно; в конце концов, для их квалификации нет никакого имени пространства имен. Для обращения к членам безымянных пространств имен невозможно использовать оператор области видимости.
Имена, определенные в безымянном пространстве имен, находятся в той же области видимости, что и область видимости, в которой определено пространство имен. Если безымянное пространство имен определяется в наиболее удаленной области видимости файла, то имена в безымянном пространстве имен должны отличаться от имен,
определенных в глобальной области видимости: int i; // глобальное объявление для i namespace { int i;
}
// неоднозначность: определено глобально и в не вложенном, безымянном
// пространстве имен i = 10;
Во всем остальном члены безымянного пространства имен являются обычными сущностями программы. Безымянное пространство имен, как и любое другое пространство имен, может быть вложено в другое пространство имен. Если безымянное пространство имен вкладывается, то к содержащимся в нем именам обращаются обычным способом, используя имена окружающего пространства имен: namespace local { namespace { int i;
}
}
// ok: i определено во вложенном безымянном пространстве имен
//
Page 988/1103
отдельно от глобального i local::i = 42; Безымянные пространства имен вместо статических файловых объектов
До введения пространств имен в стандарт С++, чтобы сделать имена локальными для файла,
их приходилось объявлять статическими (static). Применение статических файловых объектов (file static) унаследовано от языка С. В языке С объявленный статическим глобальный объект был невидим вне того файла, в котором он объявлен.
В соответствии со стандартом С++ применение объявлений статических файловых объектов не рекомендуется. Вместо них используются безымянные пространства имен. Упражнения раздела 18.2.1
Упражнение 18.12. Организуйте программы, написанные в упражнениях каждой из глав, в их собственные пространства имен. Таким образом, пространство имен chapter15 содержало бы код для программы запросов, a chapter10 — код приложения TextQuery. Используя эту структуру, откомпилируйте примеры кода приложения Query.

1   ...   46   47   48   49   50   51   52   53   54


написать администратору сайта