Изучаем c++. часть 8. библиотеки и пространства имён

Конфликт имен в C++

Допустим, что вам нужно съездить к дальним родственникам в другой город. У вас есть только их адрес: г. Ржев, ул. Вербовая, 13. Попав в город Ржев, вы открываете Google Карты/Яндекс.Карты и видите, что есть две улицы с названием Вербовая, ещё и в противоположных концах города! Какая из них нужна вам? Если у вас нет никакой дополнительной информации (например, вы знаете, что их дом находится возле аптеки или школы), вам придётся позвонить им и спросить. Чтобы подобной путаницы не возникало, все названия улиц в городе должны быть уникальными.


Аналогично и в C++, все идентификаторы (имена переменных/функций/классов и т.д.) должны быть уникальными. Если в вашей программе находятся два одинаковых идентификатора, то будьте уверены, ваша программа не скомпилируется: вы получите ошибку конфликта имен.

Пример конфликта имен:

a.cpp:

#include <iostream> void doSomething(int x) { std::cout << x; }

1 2 3 4 5 6

#include <iostream>

voiddoSomething(intx)

{

std::cout<<x;

}

b.cpp:

#include <iostream> void doSomething(int x) { std::cout << x * 2; }

1 2 3 4 5 6

#include <iostream>

voiddoSomething(intx)

{

std::cout<<x *2;

}

main.cpp:

void doSomething(int x); // предварительное объявление функции doSomething() int main() { doSomething(5); return 0; }

1 2 3 4 5 6 7 8

voiddoSomething(intx);// предварительное объявление функции doSomething()

intmain()

{

doSomething(5);

return;

}

По отдельности, файлы a.cpp, b.cpp и main.cpp скомпилируются. Однако, если a.cpp и b.cpp разместить в одном проекте — произойдет конфликт имен, так как определение функции doSomething() находится сразу в обоих файлах.

Большинство конфликтов имен происходят в двух случаях:

   Файлы, добавленные в один проект, имеют функцию (или глобальную переменную) с одинаковыми именами (ошибка на этапе ).

   Файл .cpp подключает заголовочный файл, в котором идентификатор конфликтует с идентификатором из файла .cpp (ошибка на этапе компиляции).

Как только программы становятся больше, то и идентификаторов используется больше. Следовательно, вероятность возникновения конфликта имен значительно возрастает. Хорошая новость заключается в том, что C++ предоставляет достаточно механизмов для предотвращения возникновения конфликтов имен (например, локальная область видимости или пространства имен).

Name conflicts

Element names are defined by the developer. This often results in a conflict when trying to mix XML documents from different XML applications.

This XML carries HTML table information:

<table>
    <tr>
        <td>Apples</td>
        <td>Oranges</td>
    </tr>
</table>

This XML carries information about a table (i.e. a piece of furniture):

<table>
    <name>African Coffee Table</name>
    <width>80</width>
    <length>120</length>
</table>

If these XML fragments were added together, there would be a name conflict. Both contain a element, but the elements have different content and meaning.

An XML parser will not know how to handle these differences.

Solution via prefix

Name conflicts in XML can easily be avoided using a name prefix.

The following XML distinguishes between information about the HTML table and furniture by prefixing «h» and «f» at the beginning of the elements.

<h:table>
    <h:tr>
        <h:td>Apples</h:td>
        <h:td>Oranges</h:td>
    </h:tr>
</h:table>

<f:table>
    <f:name>African Coffee Table</f:name>
    <f:width>80</f:width>
    <f:length>120</f:length>
</f:table>

Defining a Namespace


A namespace definition begins with the keyword namespace followed by the namespace name as follows −

namespace namespace_name {
   // code declarations
}

To call the namespace-enabled version of either function or variable, prepend (::) the namespace name as follows −

name::code;  // code could be variable or function.

Let us see how namespace scope the entities including variable and functions −

#include <iostream>
using namespace std;

// first name space
namespace first_space {
   void func() {
      cout << "Inside first_space" << endl;
   }
}

// second name space
namespace second_space {
   void func() {
      cout << "Inside second_space" << endl;
   }
}

int main () {
   // Calls function from first name space.
   first_space::func();
   
   // Calls function from second name space.
   second_space::func(); 

   return 0;
}

If we compile and run above code, this would produce the following result −

Inside first_space
Inside second_space

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

В первых версиях C++ все идентификаторы из Стандартной библиотеки C++ (такие как cin/cout и т.д.) можно было использовать напрямую. Тем не менее, это означало, что любой идентификатор из Стандартной библиотеки С++ потенциально мог конфликтовать с именем, которое вы выбрали для ваших собственных идентификаторов. Код, который работал, мог внезапно получить конфликт имен при подключении нового заголовочного файла из Стандартной библиотеки С++. Или, что ещё хуже, код, написанный по стандартам одной версии С++, мог уже не работать в новой версии С++. Чтобы устранить данную проблему, весь функционал Стандартной библиотеки С++ перенесли в специальную область — пространство имен (англ. «namespace»).

Аналогично тому, как город гарантирует, что все улицы в его пределах имеют уникальные названия, так и пространство имен гарантирует, что все его идентификаторы — уникальны.

Таким образом, состоит из двух частей: идентификатор и пространство имен . Весь функционал Стандартной библиотеки C++ определен внутри пространства имен (сокр. от англ. «standard»).

Мы ещё поговорим о пространствах имен на следующих уроках, а также рассмотрим создание своего собственного пространства имен. Сейчас, главное, что вам нужно запомнить, — это то, что всякий раз, когда вы используете идентификаторы из Стандартной библиотеки С++ (например, cout), вы должны сообщать компилятору, что этот идентификатор находится внутри пространства имен std.

Правило: При использовании идентификаторов из пространства имен — указывайте используемое пространство имен.

Naming system

A name in a namespace consists of a namespace identifier and a local name. The namespace name is usually applied as a prefix to the local name.

In augmented Backus–Naur form:

name = <namespace identifier> separator <local name>

When local names are used by themselves, name resolution is used to decide which (if any) particular name is alluded to by some particular local name.

Examples

Examples of names in a namespace
Context Name Namespace identifier Local name
Path /home/user/readme.txt /home/user (parent path) readme.txt (file name)
Domain name www.example.com example.com (subdomain name) www (leaf domain name)
std::array std array
UN/LOCODE US NYC US (country) NYC (locality)
XML body
$DBI::errstr DBI $errstr
Java java.util.Date java.util Date
Uniform Resource Name (URN) fi-fe19991055
Handle System 10.1000/182 10 (handle naming authority) 1000/182 (handle local name)
Digital object identifier 10.1000/182 10.1000 (publisher) 182 (publication)
MAC address 01-23-45-67-89-ab 01-23-45 (organizationally unique identifier) 67-89-ab (NIC specific)
PCI ID 1234 abcd 1234 (vendor ID) abcd (device ID)
2341 003f 2341 (vendor ID) 003f (product ID)
SPARQL dbr:Sydney Sydney

Delegation

Delegation of responsibilities between parties is important in real-world applications, such as the structure of the World Wide Web. Namespaces allow delegation of identifier assignment to multiple name issuing organisations whilst retaining global uniqueness. A central Registration authority registers the assigned namespace identifiers allocated. Each namespace identifier is allocated to an organisation which is subsequently responsible for the assignment of names in their allocated namespace. This organisation may be a name issuing organisation that assign the names themselves, or another Registration authority which further delegates parts of their namespace to different organisations.

Hierarchy

A naming scheme that allows subdelegation of namespaces to third parties is a hierarchical namespace.

A hierarchy is recursive if the syntax for the namespace identifiers is the same for each subdelegation. An example of a recursive hierarchy is the Domain name system.

An example of a non-recursive hierarchy are Uniform Resource Name representing an Internet Assigned Numbers Authority (IANA) number.

Registry Registrar Example Identifier Namespace identifier Namespace
Uniform Resource Name (URN) Internet Assigned Numbers Authority urn
Formal URN namespace Internet Assigned Numbers Authority ISBN
International Article Number (EAN) GS1 978-3-16-148410-0 978 Bookland
International Standard Book Number (ISBN) International ISBN Agency 3-16-148410-X 3 German-speaking countries
3-16-148410-X 16 Mohr Siebeck

Namespace versus scope

A namespace identifier may provide context (scope in computer science) to a name, and the terms are sometimes used interchangeably. However, the context of a name may also be provided by other factors, such as the location where it occurs or the syntax of the name.

Examples of naming systems with local and global scope, and with and without namespaces
Without a namespace With a namespace
Local scope Vehicle registration plate Filesystem Hierarchy Standard
Global scope Universally unique identifier Domain Name System

Эмуляция пространств имён

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

png_create_write_struct
png_get_signature
png_read_row
png_set_invalid

Это дает обоснованную гарантию того, что идентификаторы будут уникальны и таким образом могут быть использованы в больших программах без опасения коллизии имен идентификаторов.

К недостаткам эмуляции пространств имён можно отнести[источник не указан 1841 день]:

  • Отсутствие нормального учёта вложенных пространств; идентификаторы становятся чересчур длинными.
  • Программисты или организации могут использовать резко несовместимые соглашения о наименовании, тем самым потенциально провоцируя большую запутанность.
  • Сложные операции или операции запроса над группами идентификаторов, основанных на пространствах имён, в которых они объявлены, обрабатываются слишком неоптимально или вообще невыполнимы.
  • Все вызовы идентификаторов должны на самом деле осуществлять с полным именем пространств (англ.)русск.. Языки с непосредственной поддержкой пространств имён обычно предоставляют программисту возможность предварительно объявлять, что они хотят использовать некоторые (а то и все) идентификаторы в программе только из одного пространства, которые они впоследствии могут использовать без указания принадлежности к пространству.

Declaring a Namespace

Namespaces are declared at the beginning of a file using the keyword:

Declare a namespace called Html:

namespace Html;

Note: A declaration must be the first thing in the PHP file. The following code would be invalid:

<?phpecho «Hello World!»;namespace Html;…?>

Constants, classes and functions declared in this file will belong to the Html namespace:

Example

Create a Table class in the Html namespace:

<?phpnamespace Html;class Table {  public $title = «»;  public $numRows = 0;  public function message() {    echo «<p>Table ‘{$this->title}’ has {$this->numRows} rows.</p>»;  }}$table = new Table();$table->title = «My table»;$table->numRows = 5;?><!DOCTYPE html> <html><body><?php $table->message(); ?></body></html>

For further organization, it is possible to have nested namespaces:

Локализация

Namespace names and aliases can be localized by developers. To request the localization of namespace names, please file a MediaWiki internationalization task at Фабрикатор. In the description of the task, you should provide translations of all the namespaces in the table to the right, as well as any additional namespaces on your wiki (such as «Module» or «Gadget»).

If the site’s language is Chinese, then namespace names are translated only when the content is converted and there is no or magic words on a page.

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

Каноническое имя Локализованное имя
Media Медиа
Special Служебная
(Main)
Talk Обсуждение
User Участник
User talk Обсуждение участника
Project Проект
Project talk Обсуждение проекта
File Файл
File talk Обсуждение файла
MediaWiki MediaWiki
MediaWiki talk Обсуждение MediaWiki
Template Шаблон
Template talk Обсуждение шаблона
Help Справка
Help talk Обсуждение справки
Category Категория
Category talk Обсуждение категории

Названия пространств имён могут быть переведены на другие языки, помимо английского, а отдельные пространства имен могут быть переименованы администраторами вики-сайта (с помощью инструмента Namespace manager). Эти имена известны как «локализованные». Тем не менее, у всех пространств имен есть т.н. ‘каноническое имя’ — оригинальное английское название, которое будет работать в любой инсталляции MediaWiki. Так, ссылка на User:Foo всегда приведёт вас на страницу участника Foo, будь то вики на французском языке (где локализованное имя будет Utilisateur:Foo), русском (Участник:Foo), или хинди (सदस्य:Foo).

Канонические пространства имен приведены в таблице справа вместе с их локализованными именами для этой вики.

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

В некоторых вики есть также «псевдонимы» пространств имен: альтернативные имена, которые также будут превращаться в локализованные имена. Например, в вики можно определить «Ш» в качестве псевдонима для «Template», таким образом Ш:Страница эквивалентно Template:Страница, экономя несколько символов и секунд. Конкретный пример можно увидеть в русской Википедии, где «ВП» — псевдоним для пространства имен «Project» (соответствует англоязычному пространству имён «Wikipedia»).

По-умолчанию, «Image» («Изображение») это псевдоним «File», так эквивалентно .

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

See Руководство:Использование собственных пространств имён for more information.

Некоторые вики могут организовать свой ​​контент выходя за рамки стандартного набора имен. Вики может определить дополнительные пространства имен для содействия этому; эти пространства имен ведут себя точно так, как нормальные имена, и, как правило, имеют показатели, превышающие или равные 100. Эти пользовательские пространства имен вообще не имеют специальных свойств.

Предотвращение конфликтов имен с помощью пространств имен

С технической точки зрения применять в C# ключевое слово using при ссылках на типы, определенные во внешних пространствах имен, вовсе не требуется. Вместо этого можно использовать полностью уточненное имя типа, под которым понимается имя типа с идущим перед ним в качестве префикса названием пространства имен, где этот тип определен. Так код файла Program.cs можно видоизменить следующим образом:

Обычно необходимости в применении полностью уточненного имени не возникает. Это требует большего объема клавиатурного ввода, при этом никак не влияет на размер и скорость выполнения кода. С другой стороны, в CIL-коде типы всегда определяются с использованием полностью уточненных имен. В результате применение в C# ключевого слова using, по сути, позволяет просто экономить время.

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

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

Ключевое слово using в C# позволяет создавать псевдонимы для длинных полностью уточненных имен и потому может применяться для разрешения конфликтов на уровне имен, которые могут возникать в результате импорта пространств имен, содержащих типы с идентичными именами.

Эмуляция пространств имён

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

png_create_write_struct
png_get_signature
png_read_row
png_set_invalid

Это дает обоснованную гарантию того, что идентификаторы будут уникальны и таким образом могут быть использованы в больших программах без опасения коллизии имен идентификаторов.

К недостаткам эмуляции пространств имён можно отнести[источник не указан 1803 дня]:

  • Отсутствие нормального учёта вложенных пространств; идентификаторы становятся чересчур длинными.
  • Программисты или организации могут использовать резко несовместимые соглашения о наименовании, тем самым потенциально провоцируя большую запутанность.
  • Сложные операции или операции запроса над группами идентификаторов, основанных на пространствах имён, в которых они объявлены, обрабатываются слишком неоптимально или вообще невыполнимы.
  • Все вызовы идентификаторов должны на самом деле осуществлять с полным именем пространств (англ.)русск.. Языки с непосредственной поддержкой пространств имён обычно предоставляют программисту возможность предварительно объявлять, что они хотят использовать некоторые (а то и все) идентификаторы в программе только из одного пространства, которые они впоследствии могут использовать без указания принадлежности к пространству.

Конфликт имен

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

Рассмотрим пример такого конфликта. boo.h и doo.h — это заголовочные файлы с функциями, которые выполняют разные вещи, но имеют одинаковые имена и параметры.

boo.h:

// Функция doOperation() выполняет операцию сложения своих параметров int doOperation(int a, int b) { return a + b; }

1 2 3 4 5

// Функция doOperation() выполняет операцию сложения своих параметров

intdoOperation(inta,intb)

{

returna+b;

}


doo.h:

// Функция doOperation() выполняет операцию вычитания своих параметров int doOperation(int a, int b) { return a — b; }

1 2 3 4 5

// Функция doOperation() выполняет операцию вычитания своих параметров

intdoOperation(inta,intb)

{

returna-b;

}

main.cpp:

#include <iostream> #include «boo.h» #include «doo.h» int main() { std::cout << doOperation(5, 4); // какая версия doOperation() выполнится здесь? return 0; }

1 2 3 4 5 6 7 8 9

#include <iostream> #include «boo.h» #include «doo.h»

intmain()

{

std::cout<<doOperation(5,4);// какая версия doOperation() выполнится здесь?

return;

}

Если boo.h и doo.h скомпилировать отдельно, то всё пройдет без инцидентов. Однако, соединив их в одной программе, мы подключим две разные функции, но с одинаковыми именами и параметрами, в одну область видимости (глобальную), а это, в свою очередь, приведет к конфликту имен. В результате, компилятор выдаст ошибку. Для решения подобных проблем и добавили в язык С++ такую концепцию, как пространства имен.

Пространства имен с одинаковыми названиями

Допускается объявление пространств имен в нескольких местах (либо в нескольких файлах, либо в нескольких местах внутри одного файла). Всё, что находится внутри одного блока имен, считается частью только этого блока.

add.h:

namespace DoMath { // Функция add() является частью пространства имен DoMath int add(int x, int y) { return x + y; } }

1 2 3 4 5 6 7 8

namespaceDoMath

{

// Функция add() является частью пространства имен DoMath

intadd(intx,inty)

{

returnx+y;

}

}

subtract.h:

namespace DoMath { // Функция subtract() является частью пространства имен DoMath int subtract(int x, int y) { return x — y; } }

1 2 3 4 5 6 7 8

namespaceDoMath

{

// Функция subtract() является частью пространства имен DoMath

intsubtract(intx,inty)

{

returnx-y;

}

}

main.cpp:

#include «add.h» // импортируем DoMath::add() #include «subtract.h» // импортируем DoMath::subtract() int main(void) { std::cout << DoMath::add(5, 4) << ‘\n’; std::cout << DoMath::subtract(5, 4) << ‘\n’; return 0; }

1 2 3 4 5 6 7 8 9 10

#include «add.h» // импортируем DoMath::add() #include «subtract.h» // импортируем DoMath::subtract()

intmain(void)


{

std::cout<<DoMath::add(5,4)<<‘\n’;

std::cout<<DoMath::subtract(5,4)<<‘\n’;

return;

}

Всё работает, как нужно.

Стандартная библиотека C++ широко использует эту особенность, поскольку все заголовочные файлы, которые находятся в ней, реализуют свой функционал внутри пространства имен std.

Пространства имён в C++

Пространство имён (англ. namespace) — это группа взаимосвязанных функций, переменных, констант, классов, объектов и других компонентов программы.

С самого начала изучения C++ мы используем команду std: cout, чтобы выводить данные в терминал. На самом деле команда называется просто cout, а std — это пространство имён, в котором она находится.

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

Вот пример создания пространства имён:

Мы объединяем в группу несколько разных команд и избегаем конфликтов имён. Это нужно, когда в какой-то из подключённых вами библиотек уже есть функция, например sum (). По пространству имён программа поймёт, какая именно функция вам нужна.

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

В данном случае команда говорит, что вам нужны имена из mynames и std, поэтому никакой ошибки выведено не будет.

Также после using можно указать не целое пространство имён, а только отдельную функцию или переменную:

Nested Namespaces

Namespaces can be nested where you can define one namespace inside another name space as follows −

namespace namespace_name1 {
   // code declarations
   namespace namespace_name2 {
      // code declarations
   }
}

You can access members of nested namespace by using resolution operators as follows −

// to access members of namespace_name2
using namespace namespace_name1::namespace_name2;

// to access members of namespace:name1
using namespace namespace_name1;

In the above statements if you are using namespace_name1, then it will make elements of namespace_name2 available in the scope as follows −

#include <iostream>
using namespace std;

// first name space
namespace first_space {
   void func() {
      cout << "Inside first_space" << endl;
   }
   
   // second name space
   namespace second_space {
      void func() {
         cout << "Inside second_space" << endl;
      }
   }
}

using namespace first_space::second_space;
int main () {
   // This calls function from second name space.
   func();
   
   return 0;
}

If we compile and run above code, this would produce the following result −

Inside second_space

Previous Page Print Page

Next Page  

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

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

Ниже приведена общая форма оператора ::.

псевдоним_пространства_имен :: идентификатор

Здесь псевдоним_пространства_имен обозначает конкретное имя псевдонима пространства имен, а идентификатор — имя члена этого пространства.

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

Есть несколько способов, с помощью которых можно узнать пространство имен конкретной страницы:

Волшебные слова

magic word вернет значение пространства страницы.

To refer to another namespace, use (e.g. ), or / to refer to the namespace paired with the current one.

JavaScript

Переменная JavaScript содержит полное имя пространства имен. Переменная содержит числовой индекс пространства имен.

CSS

Тег в HTML странице вернет два класса CSS отличающихся по именам: Класс , где # это индекс пространства имен, и , где XXX это «» для всех , «» для страниц в пространстве Special, и «» для . Так CSS код, приведенный далее может быть использован для изменения внешнего вида объекта, исходя из его имени:

.ns-subject a {   /* Почти все ссылки на предметные страницы будут зелеными. */
    color #0f0;
}
.ns-talk a {      /* Ссылки на страницы обсуждения будут синими. */
    color #00f;
}
.ns-3 a {         /* и ссылки на страницы обсуждения пользователей — красными */
    color #f00;
}

С помощью MediaWiki API вы можете получить полный список всех используемых в этой вики пространств имён вместе с некоторыми настройками для каждого из них: For the system properties of each namespace, use the MediaWiki API:

API

Заключение

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

Если вы часто работаете над однотипными проектами, можете написать для себя библиотеку и подключать её во все проекты, чтобы ускорить работу. Однако новичкам стоит стараться писать код каждый раз заново — так вы сможете его постоянно переосмысливать и замечать то, чего раньше не замечали.

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


С этим читают