В этой статье рассматривается порядок создания многофайловой сборки и приводится код, иллюстрирующий каждый шаг процедуры.
Интегрированную среду разработки в Visual Studio для C# и Visual Basic можно использовать только для создания однофайловых сборок. Если требуется создать многофайловую сборку, необходимо использовать компиляторы командной строки или Visual Studio с Visual C++. Многофайловые сборки поддерживаются только .NET Framework.
Создание многофайловой сборки
- Скомпилируйте в модули кода все файлы, содержащие пространства имен, на которые имеются ссылки в других модулях сборки. По умолчанию для модулей кода используется расширение NETMODULE. Предположим, например, что файл Stringer имеет пространство имен myStringer , которое содержит класс с именем Stringer . Класс Stringer содержит метод с именем StringerMethod , который выводит отдельную строку на консоль.
// Assembly building example in the .NET Framework. using namespace System; namespace myStringer < public ref class Stringer < public: void StringerMethod() < System::Console::WriteLine(«This is a line from StringerMethod.»); >>; >
// Assembly building example in the .NET Framework. using System; namespace myStringer < public class Stringer < public void StringerMethod() < System.Console.WriteLine(«This is a line from StringerMethod.»); >> >
‘ Assembly building example in the .NET Framework.
Namespace myStringer Public Class Stringer Public Sub StringerMethod() System.Console.WriteLine(«This is a line from StringerMethod.») End Sub End Class End Namespace
cl /clr:pure /LN Stringer.cpp
csc /t:module Stringer.cs
vbc /t:module Stringer.vb
Язык C++ с нуля | #35 Разделение программного кода на несколько файлов в c++
#using «Stringer.netmodule» using namespace System; using namespace myStringer; //The namespace created in Stringer.netmodule. ref class MainClientApp < // Static method Main is the entry point method. public: static void Main() < Stringer^ myStringInstance = gcnew Stringer(); Console::WriteLine(«Client code executes»); myStringInstance->StringerMethod(); > >; int main()
using System; using myStringer; class MainClientApp < // Static method Main is the entry point method. public static void Main() < Stringer myStringInstance = new Stringer(); Console.WriteLine(«Client code executes»); myStringInstance.StringerMethod(); >>
Imports myStringer Class MainClientApp ‘ Static method Main is the entry point method. Public Shared Sub Main() Dim myStringInstance As New Stringer() Console.WriteLine(«Client code executes») myStringInstance.StringerMethod() End Sub End Class
cl /clr:pure /FUStringer.netmodule /LN Client.cpp
csc /addmodule:Stringer.netmodule /t:module Client.cs
vbc /addmodule:Stringer.netmodule /t:module Client.vb
Укажите параметр /t:module, поскольку этот модуль будет добавлен в сборку на следующем шаге. Также укажите параметр /addmodule, так как код в Client ссылается на пространство имен, созданное кодом в Stringer.netmodule. Компилятор создает модуль с именем Client.netmodule, который содержит ссылку на другой модуль — Stringer.netmodule.
Многофайловый проект | Изучение С++ для начинающих. Урок #139
Примечание Компиляторы C# и Visual Basic поддерживают непосредственное создание многофайловых сборок с помощью следующих двух синтаксических структур. Для создания сборки из двух файлов используются две компиляции:
cl /clr:pure /LN Stringer.cpp cl /clr:pure Client.cpp /link /ASSEMBLYMODULE:Stringer.netmodule
csc /t:module Stringer.cs csc Client.cs /addmodule:Stringer.netmodule
vbc /t:module Stringer.vb vbc Client.vb /addmodule:Stringer.netmodule
При создании сборки из двух файлов используется одна компиляция:
cl /clr:pure /LN Stringer.cpp cl /clr:pure Client.cpp /link /ASSEMBLYMODULE:Stringer.netmodule
csc /out:Client.exe Client.cs /out:Stringer.netmodule Stringer.cs
vbc /out:Client.exe Client.vb /out:Stringer.netmodule Stringer.vb
al Client.netmodule Stringer.netmodule /main:MainClientApp.Main /out:myAssembly.exe /target:exe
См. также
- Создание сборок
- Практическое руководство. Просмотр содержимого сборки
- Обнаружение сборок в среде выполнения
- Многофайловые сборки
Источник: learn.microsoft.com
Многофайловые проекты 
Создание программы на языке С, состоящей из нескольких файлов
Программа на языке С – это совокупность функций Запуск любой программы начинается с запуска главной функции, содержащей в себе всю остальную часть программы. Внутри главной функции для реализации заданного алгоритма вызываются все другие необходимые функции.
Часть функций создается самим программистом, другая часть – библиотечные функции – поставляется пользователю со средой программирования и используется в процессе разработки программ (например, printf(), sqrt() и др.). Простейший метод использования нескольких функций требует их размещения в одном и том же файле.
Затем выполняется компиляция этого файла, как если бы он содержал единственную функцию. Другие подходы к решению этой проблемы существенно зависят от конкретной операционной системы (Unix-подобные системы, Windows, Macintosh). Компиляторы операционных систем Windows и Macintosh представляют собой компиляторы, ориентированные на проекты. Проект описывает ресурсы, используемые программой. Эти ресурсы включают файлы исходного программного кода.
Создание программы на языке С, состоящей из нескольких файлов
Если поместить главную функцию main() в один файл, а определения собственной функции программиста – во второй файл, то первому файлу нужны прототипы функций. Для этого можно хранить прототипы функций в одном из заголовочных файлов. Хороший тон в программировании рекомендует размещать прототипы функций и объявлять их константы в заголовочном файле. Назначение отдельных задач отдельным функциям способствует улучшениям программы.
Внешние и статические функции
К внешней функции доступ могут осуществлять функции из других файлов Статическая функция может использоваться только в файле, в котором она определена. Например, возможны следующие объявления функций: double gamma(); // внешняя функция по умолчанию static double beta(); extern double delta(); Функция gamma() и delta() могут использоваться функциями из других файлов, которые являются частью программы, тогда как beta() – нет. В силу этого применение функции beta() ограничено одним файлом, поэтому в других файлах можно использовать функции с тем же именем. Одна из причин использования класса статической памяти заключается в необходимости создания функций, приватных для конкретных модулей, благодаря чему во многих случаях удается избежать конфликта имен. Обычная практика состоит в том, что при объявлении функции, определенной в другом файле, указывается
Золотое правило для надежного программирования
Принцип «необходимости знать», или принцип минимально необходимой области видимости Рекомендуется держать всю внутреннюю работу каждой функции максимально закрытой по отношению к другим функциям, используя совместно только те переменные, без которых нельзя обойтись по логике программы. Другие классы памяти полезны, и ими можно воспользоваться. Однако всякий раз следует задать вопрос: а есть ли в этом необходимость?
Характеристики памяти, использованной для хранения данных
Продолжительность хранения может быть статической , автоматической или распределенной . Если продолжительность хранения статическая, память распределяется в начале выполнения программы и остается занятой на протяжении всего выполнения. Если продолжительность хранения автоматическая, то память под переменную выделяется в момент, когда выполнение программы входит в блок, в котором эта переменная определена, и освобождается, когда выполнение программы покидает этот блок.
Если память выделяется, то она выделяется с помощью функции malloc() (или родственной функции) и освобождается посредством функции free(). Область видимости определяет, какая часть программы может получить доступ к данным. Переменные, определенные вне пределов функции, имеют область видимости в пределах файла и видимы в любой функции, определенной после объявления этой переменной.
Переменная, определенная в блоке или как параметр функции, видима только в этом блоке и в любом из блоков, вложенных в этот блок. Связывание описывает экстент (протяжение, пространство), в пределах которого переменная, определенная в одной части программы, может быть привязана к любой другой части программы. Переменная с областью видимости в пределах блока, будучи локальной, не имеет связывания. Переменная с областью видимости в пределах файла имеет внутреннее или внешнее связывание. Внутреннее связывание означает, что
Спецификаторы хранения
Стандарт С поддерживает четыре спецификатора класса памяти (хранения): extern static register auto Эти спецификаторы сообщают компилятору, как он должен разместить соответствующие переменные в памяти. Общая форма объявления переменных при этом такова: спецификатор_класса_памяти тип имя переменой; Спецификатор класса памяти в объявлении всегда должен стоять первым.
Спецификатор extern
В языке С при редактировании связей к переменной может применяться одно из трех связываний: внутреннее , внешнее или же не относящееся ни к одному из этих типов . В общем случае к именам функций и глобальных переменных применяется внешнее связывание. Это означает, что после компоновки они будут доступны во всех файлах, составляющих программу.
К объектам, объявленным со спецификатором static и видимым на уровне файла, применяется внутренне связывание, после компоновки они будут доступны только внутри файла, в котором они объявлены. К локальным переменным связывание не применяется и поэтому они доступны только внутри своих блоков.
Спецификатор extern указывает на то, что к объекту применяется внешнее связывание, именно поэтому они будут доступны во всей программе. Объявление (декларация) объявляет имя и тип объекта. Определение (описание, дефиниция) выделяет для объекта участок памяти, где он будет находиться. Один и тот же объект может быть объявлен неоднократно в разных местах, но описан он может быть только один раз.
Пример использования спецификатора extern при использовании глобальных переменных:
#include #include // Главная функция int main (void) < // объявление глобальных переменных extern int a, b; printf(«nt a = %d; b = %dn», a, b); printf(«n Press any key: «); _getch(); return 0; >// инициализация (описание) глобальных переменных int a = 33, b = 34; Описание глобальных переменных дано за пределами главной функции main(). Если бы их объявление и инициализация встретились перед main(), то в объявлении со спецификатором extern не было бы необходимости.
При компиляции выполняются следующие правила:
Если компилятор находит переменную, не объявленную внутри блока, он ищет ее объявление во внешних блоках. Если не находит ее там, то ищет среди объявлений глобальных переменных.
Источник: studfile.net
2.7 – Программы с несколькими файлами исходного кода
По мере того, как программы становятся больше, в целях организации или повторного использования их обычно разделяют на несколько файлов. Одним из преимуществ работы с IDE является то, что они значительно упрощают работу с несколькими файлами. Вы уже знаете, как создавать и компилировать однофайловые проекты. Добавить новые файлы в существующие проекты очень просто.
Лучшая практика
Когда вы добавляете в проект новые файлы исходного кода, давайте им расширение .cpp .
Для пользователей Visual Studio
В Visual Studio кликните правой кнопкой мыши на папке Исходные файлы (Source Files) в окне Обозревателя решений (Solution Explorer) и выберите Добавить (Add) → Создать элемент… (New Item…).
Убедитесь, что у вас выбран Файл C++ (.cpp). Дайте новому файлу имя, и он будет добавлен в ваш проект.
Примечание. Если вы создаете новый файл из меню Файл (File), а не из своего проекта в обозревателе решений, новый файл не будет добавлен в ваш проект автоматически. Вам придется добавить его в проект вручную. Для этого кликните правой кнопкой мыши на папке Исходные файлы (Source Files) в окне Обозревателя решений (Solution Explorer) и выберите Добавить (Add) → Существующий элемент (Existing Item), а затем выберите свой файл.
Теперь, когда вы компилируете свою программу, вы должны увидеть, как компилятор перечисляет имя вашего файла при компиляции.
Для пользователей Code::Blocks
В Code::Blocks перейдите в меню File (Файл) и выберите New (Создать) → File… (Файл…).
В диалоговом окне New from template (Создать из шаблона) выберите C/C++ source (Исходный файл C/C++) и нажмите Go (Перейти).
На этом этапе вы можете увидеть или не увидеть приветствие в диалоговом окне мастера создания исходного файла C/C++. Если да, щелкните Next (Далее).
На следующей странице мастера выберите C++ и нажмите Next (Далее).
Теперь дайте новому файлу имя (не забудьте расширение .cpp ) и выберите все конфигурации сборки. Наконец, выберите Finish (Готово).
Теперь, когда вы будете компилировать свою программу, вы должны увидеть, как компилятор перечисляет имя вашего файла по мере компиляции.
Для пользователей GCC/G++
Из командной строки вы можете создать дополнительный файл самостоятельно, используя свой любимый редактор, и дать ему имя. Когда вы компилируете свою программу, вам нужно будет включить в строку компиляции все соответствующие исходные файлы. Например:
g++ main.cpp add.cpp -o main
где main.cpp и add.cpp – это имена ваших исходных файлов, а main – имя выходного файла.
Пример с несколькими файлами
В уроке «2.6 – Предварительные объявления и определения» мы рассмотрели программу с одним исходным файлом, которая не компилируется:
#include int main() < std::cout int add(int x, int y)
Когда компилятор достигает вызова функции add в строке 5 в функции main , он не знает, что такое add , потому что мы определили add только в строке 9! Нашим решением было либо переупорядочить функции (поместив сначала add ), либо использовать для add предварительное объявление.
Теперь посмотрим на аналогичную программу из нескольких исходных файлов:
int add(int x, int y)
#include int main() < std::cout
Ваш компилятор может решить сначала скомпилировать либо add.cpp , либо main.cpp . В любом случае main.cpp не скомпилируется, что приведет к той же ошибке компилятора, что и в предыдущем примере:
main.cpp(5) : error C3861: ‘add’: identifier not found
Причина точно такая же: когда компилятор достигает строки 5 файла main.cpp , он не знает, что такое идентификатор add .
Помните, что компилятор компилирует каждый файл отдельно. Он не знает о содержимом других исходных файлов и не запоминает что-либо, что он видел из ранее скомпилированных исходных файлов. Таким образом, даже если компилятор, возможно, видел определение функции add ранее (если он сначала скомпилировал add.cpp ), он этого не запомнил.
Эта ограниченная видимость и короткая память являются преднамеренными, чтобы файлы могли иметь функции или переменные, у которых одинаковые имена, но которые не конфликтуют друг с другом. В следующем уроке мы рассмотрим пример такого конфликта.
Наши варианты решения здесь такие же, как и раньше: поместить определение функции add перед функцией main или удовлетворить компилятор предварительным объявлением. В этом случае, поскольку функция add находится в другом файле, вариант изменения порядка определений не подходит.
Лучшее решение здесь – использовать предварительное объявление:
main.cpp (с предварительным объявлением):
#include // необходимо, чтобы main.cpp знал, что add() — это функция, объявленная в другом месте int add(int x, int y); int main()
add.cpp (остается прежним):
int add(int x, int y)
Теперь, когда компилятор компилирует main.cpp , он будет знать, что такое идентификатор add , и будет удовлетворен. Линкер соединит вызов функции add в main.cpp с определением функции add в add.cpp .
Используя этот метод, мы можем предоставить файлам доступ к функциям, которые находятся в другом файле.
Попробуйте скомпилировать add.cpp и main.cpp с предварительным объявлением. Если вы получили ошибку линкера, убедитесь, что вы правильно добавили add.cpp в свой проект или строку компиляции.
Что-то пошло не так!
Есть много вещей, которые могут пойти не так, когда вы в первый раз попытаетесь работать с несколькими файлами. Если вы попробовали приведенный выше пример и столкнулись с ошибкой, проверьте следующее:
1. Если вы получаете ошибку компилятора о том, что add не определена в main , вы, вероятно, забыли предварительное объявление для функции add в main.cpp .
2. Если вы получаете сообщение об ошибке компоновщика о том, что add не определена, например
2а. … наиболее вероятная причина в том, что add.cpp неправильно добавлен в ваш проект. При компиляции вы должны увидеть в списке компиляции и main.cpp , и add.cpp . Если вы видите только main.cpp , значит add.cpp определенно не компилируется. Если вы используете Visual Studio или Code::Blocks, вы должны увидеть add.cpp в списке в обозревателе решений / на панели проекта в левой части IDE. Если его не видно, кликните на проекте правой кнопкой мыши и добавьте этот файл, а затем попробуйте скомпилировать снова. Если вы компилируете из командной строки, не забудьте включить main.cpp и add.cpp в свою команду компиляции.
2b. … возможно, вы добавили add.cpp не в тот проект.
2c. … возможно, что файл не компилируется или не линкуется. Проверьте свойства файла и убедитесь, что файл настроен для компиляции/линковки. В Code::Blocks компиляция и линковка – это отдельные флажки, которые следует установить. В Visual Studio есть параметр «исключить из сборки» (exclude from build), для которого следует установить значение «нет» или оставить пустым.
3. Не пишите #include «add.cpp» в main.cpp . Это заставит компилятор вставить содержимое add.cpp непосредственно в main.cpp вместо того, чтобы рассматривать их как отдельные файлы.
Резюме
Когда компилятор компилирует программу из нескольких исходных файлов, он может компилировать эти файлы в любом порядке. Кроме того, он компилирует каждый файл отдельно, не зная, что находится в других файлах.