Модульность, раздельная компиляция, пространства имен, using директива
Поможем в ✍️ написании учебной работы
Поможем с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой

Модули в C++

· С целью оптимизации процесса разработки и борьбы со сложностью системы абстракций, программы на C++ разбиваются на модули (исходные файлы – единицы компиляции “.cc” и “.cpp”).

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

 

Каждый элемент имеет свой тип связи (linkage):

· no linkage (для элементов на стеке)

· internal linkage – внутренняя связь(область видимости – файл)

· external linkage – внешняя связь (область видимости – проект)

o Глобальные переменные и функции по умолчанию имеют внешнюю связь

o Только константные или статические имеют внутреннюю связь

o extern объявление позволяет подцепить переменную из другого модуля на стадии линковки, а также сделать внешнюю компоновку

o Чтобы сделать имена локальными для единицы компиляции их можно поместить в безымянное пространство имён

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

 

Поскольку пространства с глобальной областью видимости добавляются к системе, то имеется возможность возникновения коллизии имен. Это становится особенно актуальным при использо­вании библиотек, разработанных различными независимыми производителями. Использование
namespace позволяет разбить глобальное пространство имен с тем, чтобы решить подобную про­блему. По существу, namespace определяет область видимости.

 

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

 

Объявление пространства имен имеет синтаксис:

namespaсe идентификатор {

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

}

Идентификатор определяет имя пространства имен.

· Тело содержит описания и определения имен.

· Пространства имен открыты, т. е. одно и то же пространство имен можно расширять в разных исходных файлах.

· Пространства имен могут быть вложены друг в друга.

 

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

 

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

 

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

 

Для доступа к именам пространства имен используется оператор разрешения области видимости :: , который имеет синтаксис:

имя_пространства_имен ::имя_объекта

 

В этом случае имя пространства имен называется квалификатором имени объекта. Оператор разрешения области видимости выполнится на этапе компиляции программы и не влияет на ее производительность.

 

Для доступа к именам из глобального пространства имен используется оператор разрешения области видимости без квалификатора (пишем, ::имя_объекта)

int x;

void f () {

int x;

x = 1;

::x = 4;

}

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

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

 


Директива Using

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

u sing имя_пр_имен имя_объекта;

 

 

Объявление using может использоваться для вложенных пространств имен.

Для объявления сразу всех имен из некоторого пространства имен используется директива using вида:

using namespace имя _ пр _ имен ;

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

namespace псевдоним = имя_пр_имен ;

(например: using FactoryMap = std::map<Key, AbstractCreator<BaseType, Args...>*>;)

  Раздельная компиляция

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

1) Препроцессинг

Самая первая стадия компиляции программы.

Препроцессор — это макро процессор, который преобразовывает вашу программу для дальнейшего компилирования. На данной стадии происходит происходит работа с препроцессорными директивами. Например, препроцессор добавляет хэдеры в код (#include), убирает комментирования, заменяет макросы (#define) их значениями, выбирает нужные куски кода в соответствии с условиями #if, #ifdef и #ifndef.

 

2) Компиляция

На данном шаге g++ выполняет свою главную задачу — компилирует, то есть преобразует полученный на прошлом шаге код без директив в ассемблерный код. Это промежуточный шаг между высокоуровневым языком и машинным (бинарным) кодом.

 

Ассемблерный код — это доступное для понимания человеком представление машинного кода.

 

 

3) Ассемблирование

Далее, необходимо перевести ассемблерный код в машинный с помощью ассемблера.
Ассемблер преобразовывает ассемблерный код в машинный код, сохраняя его в объектном файле.

 

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

 

 

4) Компоновка

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

 

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

 

5) Загрузка

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

 




Заголовочные файлы

o используются для включения общих частей кода (обычно прототипов) для многократного использования

o обязательно нужен страж включения, чтобы избежать многократного включения хедера в одну единицу трансляции

o могут содержать:

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

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

§ определения шаблонов, inline функций, констант

§ директивы включения, макроопределения, директивы условной компиляции, комментарии

o не должны (а то Ефремов пиздить будет):

§ определения обычных функций, данных, агрегатов (массивов)

§ неименованные пространства имён, экспортируемые определения шаблонов

 

Инициализация переменных при запуске:

o Исполнение начинается с main

o Все нелокальные переменные инициализируются до его вызова

o Нет фиксированного порядка инициализации между единицами трансляции

o Внутри единицы трансляции - в порядке объявления

Дата: 2019-02-19, просмотров: 648.