По мере усложнения программ возникает необходимость хранить различные части программы в отдельных файлах. Например, функции, написанные для упражнений раздела 6.1, можно было бы сохранить в одном файле, а код, использующий их, в других файлах исходного кода. Язык С++ позволяет разделять программы на логические части, предоставляя средство, известное как раздельная компиляция (separate compilation). Раздельная компиляция позволяет разделять программы на несколько файлов, каждый из которых может быть откомпилирован независимо.
Компиляция и компоновка нескольких файлов исходного кода
Предположим, например, что определение функции fact() находится в файле fact.cc, а ее объявление — в файле заголовка Chapter6.h. Файл fact.cc, как и любой другой файл, использующий эту функцию, будет включать заголовок Chapter6.h. Функцию main(), вызывающую функцию fact(), будем хранить в еще одном файле factMain.
Чтобы создать исполнимый файл (executable file), следует указать компилятору, где искать весь используемый код. Эти файлы можно было бы откомпилировать следующим образом:
$ CC factMain.cc fact.cc # generates factMain.exe or a.out
$ CC factMain.cc fact.cc -o main # generates main or main.exe
где CC — имя компилятора; $ — системная подсказка; # — начало комментария командной строки. Теперь можно запустить исполняемый файл, который выполнит нашу функцию main().
Если бы изменен был только один из наших файлов исходного кода, то перекомпилировать достаточно было бы только тот файл, который был фактически изменен. Большинство компиляторов предоставляет возможность раздельной компиляции каждого файла. Обычно этот процесс создает файл с расширением .obj (на Windows) или .o (на UNIX), указывающим, что этот файл содержит объектный код (object code).
Компилятор позволяет скомпоновать (link) объектные файлы (object file) и получить исполняемый файл. На системе авторов раздельная компиляция программы осуществляется следующим образом:
$ CC -с factMain.cc # generates factMain.o
$ CC -c fact.cc # generates fact.o
$ CC factMain.o fact.o # generates factMain.exe or a.out
$ CC factMain.o fact.o -o main # generates main or main.exe
Сверьтесь с руководством пользователя вашего компилятора, чтобы уточнить, как именно компилировать и запускать программы, состоящие из нескольких файлов исходного кода.
Упражнения раздела 6.1.3
Упражнение 6.9. Напишите собственные версии файлов fact. cc и factMain.cc. Эти файлы должны включать заголовок Chapter6.h из упражнения предыдущего раздела. Используйте эти файлы чтобы понять, как ваш компилятор обеспечивает раздельную компиляцию.
Вопрос задан
Изменён 8 лет 8 месяцев назад
Просмотрен 2k раза
Уважаемые Господа! Я новичок. Учусь по книге С.Прата «С++ Лекции и упражнения.» При попытке собрать программу из 3 файлов в Visual C++ Express компоновщик выдаёт ошибку LNK 2019. Неразрешённый внешний символ. На одном из форумов прочитал, что требуется установить флажок «Создать файл связей». Путь примерно такой: Построение — параметры — компоновщик — изменение категории — общие — — установить флажок: «Создать файл связей». Не могу найти, где находится компоновщик. Может быть в данной среде это вообще невозможно? Буду очень признателен, если поможете. С уважением, Андрей.
Прошу прощение, что забыл, — это по первости, — добавить код. Испралвяюсь. И ещё. Я работаю в англоязычной версии Visual C++ Express.
A_Plus_f_1.cpp // Это название 1-го файла. ------------------------------------------- #include<iostream> #include"ashownz.h" using namespace std ; int main() { cout << "\n\n\n" << endl ; func_show_nz() ; cout << "\n\n\n" << endl ; return (0) ; } A_Plus_body_f_2.cpp // Это название 2-го файла. ------------------------------------------------ #include<iostream> #include"ashownz.h" void func_show_nz( void ) { using namespace std ; cout << "BBB" << endl ; } ashownz.h // Это название заголовочного файла. ---------------------------- #ifndef A_SHOW_NZ_H_ #define A_SHOW_NZ_H_ void func_show_nz( void ) ; #endif
Все ТРИ файла находятся в одной папке, которая лежит на рабочем столе. Я запускаю Visual Studio C++ 2010 Express, и добавляю ТОЛЬКО 1-й файл. Этот файл прекрасно компилируется, из чего можно сделать заключение, что все ТРИ файла компилятору видны. А вот собрать их все вместе компоновщик не может.
Вот что печатает компоновщик :
—— Build started: Project: eXPerimentCpp, Configuration: Debug Win32 —— A_Plus_f_1.obj : error LNK2019: unresolved external symbol «void __cdecl func_show_nz(void)» (?func_show_nz@@YAXXZ) referenced in function _main C:\Documents and Settings\Бит\мои документы\visual studio 2010\Projects\eXPerimentCpp\Debug\eXPerimentCpp.exe : fatal error LNK1120: 1 unresolved externals ========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
3
добавляю ТОЛЬКО 1-й файл
Собственно, в этом и проблема. Утверждение
Этот файл прекрасно компилируется, из чего можно сделать заключение, что все ТРИ файла компилятору видны.
неверно. Компилятор видит A_Plus_f_1.cpp
(потому что вы его добавили в проект) и ashownz.h
(потому что строкой #include"ashownz.h"
вы явным образом указываете, что этот файл лежит в текущей папке). О файле A_Plus_body_f_2.cpp
компилятор ничего не знает.
Компилятор (простите за тавтологию) компилирует cpp-файлы по отдельности. При этом он «вдит» функции, объявленные в текущем файле, а также сигнатуры функций, объявленные в заголовочных файлах, подключённых к данному cpp-файлу. После компиляции вы получаете объектный файл, в котором вызовы функций представляются именами функций. Затем в дело вступает компоновщик, который для каждой вызываемой функции ищет её реализацию в скомпилированных объектных файлах. Так как компилятор скомпилировал только A_Plus_f_1. cpp
, реализацию функции func_show_nz
он нигде найти не может, отсюда и ошибка. Так что добавляйте второй cpp-файл в проект, и будет вам счастье.
Регистрация через Google
Регистрация через Facebook
Регистрация через почту
Почта
Необходима, но никому не показывается
Почта
Необходима, но никому не показывается
Нажимая на кнопку «Отправить ответ», вы соглашаетесь с нашими пользовательским соглашением, политикой конфиденциальности и политикой о куки
спросил
Изменено 2 года, 3 месяца назад
Просмотрено 66 раз
я пытаюсь сделать файл Make (отдельная компиляция), но компилятор выдает мне эту ошибку
вот ошибка:
make: *** Нет правила для создания цели 'stringFunction. o', необходимой для 'stringsmake'. Останавливаться.
а вот и мой make.mak:
# Dateinname: stringsmake.mak создание строк: stringFunction.o stringMain.o gcc -o stringsmake stringFunction.o stringMain.o stringFunctions.o: stringFunctions.h stringFunctions.c gcc -c stringFunctions.c stringmain.o: stringFunctions.h Stringmain.c gcc -c Stringmain.c
5
У вас много несоответствий между разными именами файлов. Как в написании, так и в прописных и строчных буквах.
Я предлагаю вам научиться использовать неявные правила , автоматические переменные и «стандартные» переменные для создания более простых make-файлов.
Возможно, что-то вроде этого:
# Стандартный флаг для компиляции исходного кода C # Добавить опцию для дополнительных предупреждений CFLAGS = -Стена # Основная цель mystring: Stringmain. 9 расширится до$(CC) -o mystring Stringmain.o stringFunctions.o
. Вы можете добавить явные правила для списка зависимостей (например, файлов заголовков), но пропустить любую команду. Затем неявные правила будут использоваться для генерации объектных файлов.
Пожалуйста, найдите время, чтобы изучить руководство по используемой вами программе
make
(например, GNU Make).6
Приведенный ниже
Makefile 9$(LIBFLAGS) stringFunctions.o: stringFunctions.h stringMain.o: stringFunctions.h
3
Зарегистрируйтесь с помощью Google
Зарегистрироваться через Facebook
Зарегистрируйтесь, используя электронную почту и пароль
Электронная почта
Требуется, но не отображается
Электронная почта
Требуется, но не отображается
Предположим, вы создаете класс с несколькими файлами . cpp (каждый из которых содержит реализацию функции-члена) и имеете объявление класса в файле .h. Кроме того, каждый файл .cpp включает файл .h с помощью директивы include.
Мне сказали, что если вы измените реализацию любой из функций-членов (файлов .cpp), вам придется перекомпилировать каждый файл .cpp, чтобы запустить программу. То есть, если бы у меня было 5 функций-членов (каждая реализована в файле .cpp) и я изменил реализацию 1 из файлов .cpp, мне пришлось бы скомпилировать 1 файл .cpp, который я изменил, И 4 других файла .cpp, которые я не изменился, чтобы правильно запустить мою программу.
Мой вопрос, если предыдущее утверждение верно, то почему оно верно? Любое понимание этой концепции было бы полезно.
1
Это ложь. Если нет изменений в конкретном файле реализации, нет изменений в файлах заголовков, которые он включает, и нет изменений в среде или параметрах компилятора, нет абсолютно никакой необходимости перекомпилировать его. Все, что влияет на компиляцию этого файла, не изменилось.
На самом деле, вы можете скомпилировать каждый из файлов, даже не имея других. Затем вы можете связать все скомпилированные файлы вместе, при этом все файлы реализации никогда не будут находиться в одном месте.
1
Утверждение неверно. На самом деле, вся причина, по которой заставляет
(и подобные) существовать, заключается в том, что это ложно - они минимизируют время/усилия для перестроения приложения, отслеживая, какие исходные файлы были изменены, и воссоздавая только объектные файлы, которые зависят в исходных файлах, которые были изменены. Обновленные объектные файлы просто не трогают.
Как только все объектные файлы обновлены, они соединяются вместе для создания окончательного исполняемого файла (и, опять же, это обычно делается только в том случае, если хотя бы один объектный файл новее текущего исполняемого файла).
Конечно, также возможно использовать make
для заданий, не связанных с компиляцией и компоновкой, но это почти наверняка их наиболее частое применение и (по крайней мере, большая часть) причина, по которой они были изобретены ( единственное очевидное изменение состоит в том, что они изначально использовались в основном для исходного кода C, а не C++, но в этом отношении они почти неотличимы).
Единственная очевидная разница между ними заключается в том, что когда/если вы используете шаблоны, вы обычно в конечном итоге размещаете много кода в заголовках. В этом случае изменение заголовка приводит к повторной компиляции всего кода, включающего этот заголовок, что часто бывает очень много. С кодом C и/или нешаблонным кодом C++ вы помещаете большую часть кода в исходный файл, поэтому вам нужно только перекомпилировать другие файлы, если вы измените интерфейс к коду (который обычно изменяет заголовок), но только этот файл необходимо перекомпилировать, если вы ограничиваете свои изменения реализацией без изменения интерфейса.
1
Как уже говорили другие, утверждение ложно. Однако существуют интересные сложности, связанные с измененными исходными файлами, исходными зависимостями и перекомпиляцией. Наиболее полезные обсуждения этого вопроса, что неудивительно, приведены в серии постов «Гуру недели» от Херба Саттера, в которых он обсуждает изоляцию зависимостей и идиому Pimpl:
Брандмауэры компиляции
Идиома Fast Pimpl
Радость Pimpls
Эта штука кажется эзотерической почти всем, когда они впервые ее видят, но идиома Pimpl оказывается удивительно полезной на практике.