Как только программы становятся больше, их следует разбивать на несколько файлов (в целях удобства и улучшения функциональности). Одним из преимуществ использования IDE является легкость в работе с n-ным количеством файлов. Мы уже знаем, как создавать и компилировать однофайловые проекты, добавление новых файлов не составит труда.
Многофайловые проекты в Visual Studio
В Visual Studio щелкните правой кнопкой мыши по имени вашего проекта в "Обозревателе решений"
, затем "Добавить" > "Создать элемент..."
:
Во всплывающем диалоговом окне выберите тип файла, укажите его имя, расположение, а затем нажмите "Добавить"
:
Также вы можете добавлять файлы к вашему проекту через "Проект" > "Добавить новый элемент..."
:
Многофайловые проекты в Code::Blocks
В Code::Blocks перейдите в "File" > "New" > "File..."
:
Затем выберите "C/C++ source"
и нажмите "Go"
:
Затем "Next"
(этого окна может и не быть):
Затем "C++"
и опять "Next"
:
Затем укажите имя нового файла (не забудьте расширение .cpp) и его расположение (нажмите на троеточие и выберите путь). Убедитесь, что поставлены все три галочки (они отвечают за конфигурации сборки). Затем нажмите "Finish"
:
Многофайловые проекты в GCC/G++
В командной строке вам нужно будет создать файл, указать его имя и подключить к компиляции, например:
g++ main.cpp add.cpp -o main
(где main.cpp и add.cpp — это имена файлов с кодом, а main — это имя файла-результата)
Пример многофайловой программы
Рассмотрим следующую программу, которая состоит из двух файлов.
add.cpp:
1 2 3 4 |
int add(int x, int y) { return x + y; } |
main.cpp:
1 2 3 4 5 6 7 |
#include <iostream> int main() { std::cout << "The sum of 3 and 4 is: " << add(3, 4) << std::endl; return 0; } |
Попробуйте запустить эту программу. Она не скомпилируется, вы получите следующую ошибку:
add: идентификатор не найден
При компиляции кода, компилятор не знает о существовании функций, которые находятся в других файлах. Это сделано специально, чтобы функции и переменные с одинаковыми именами, но в разных файлах, не вызывали конфликт имен.
Тем не менее, в данном случае, мы хотим, чтобы main.cpp знал (и использовал) функцию аdd(), которая находится в add.cpp. Для предоставления доступа main.cpp к функциям add.cpp, нам нужно использовать предварительное объявление:
1 2 3 4 5 6 7 8 9 |
#include <iostream> int add(int x, int y); // это нужно для того, чтобы main.cpp знал, что функция add() определена в другом месте int main() { std::cout << "The sum of 3 and 4 is: " << add(3, 4) << std::endl; return 0; } |
Теперь, когда компилятор будет компилировать main.cpp, он будет знать, что такое add(). Попробуйте запустить эту программу еще раз.
Что-то пошло не так!
Есть много вещей, которые могут пойти не так, особенно, если вы это делаете в первый раз. Главное — не паниковать:
Пункт №1: Если вы получили ошибку от компилятора, что функция add() не определена в main(), то, скорее всего, вы забыли записать предварительное объявление функции add() в main.cpp.
Пункт №2: Если вы получили следующую ошибку от линкера:
unresolved external symbol "int __cdecl add(int,int)" (?add@@YAHHH@Z) referenced in function _main
то возможных решений есть несколько:
a) Cкорее всего, add.cpp некорректно добавлен в ваш проект. Если вы используете Visual Studio или Code::Blocks, то вы должны увидеть add.cpp в "Обозревателе решений"
в списке файлов вашего проекта или в панели проекта IDE. Если добавленного файла нет, то щелкните правой кнопкой мыши по вашему проекту и добавьте файл, как это показано выше, а затем повторите попытку компиляции вашего проекта.
б) Вполне возможно, что вы добавили add.cpp к другому проекту.
в) Вполне возможно, что добавленный файл не подключен к компиляции/линкингу. Щелкните правой кнопкой мыши по имени вашего добавленного файла и выберите "Свойства"
:
Убедитесь, что пункт "Исключен из сборки"
оставлен пустым или выбрано значение "Нет"
:
Пункт №3: Не следует писать следующую строку в main.cpp:
1 |
#include "add.cpp" |
Наличие этой строки приведет к тому, что компилятор вставит всё содержимое add.cpp непосредственно в main.cpp вместо того, чтобы рассматривать эти файлы как отдельные.
Тест
Разделите следующую программу на два файла (main.cpp и input.cpp): main.cpp должен содержать функцию main(), а input.cpp — функцию getInteger().
Помните, что для функции getInteger() вам понадобится предварительное объявление в main.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#include <iostream> int getInteger() { std::cout << "Enter an integer: "; int x; std::cin >> x; return x; } int main() { int x = getInteger(); int y = getInteger(); std::cout << x << " + " << y << " is " << x + y << '\n'; return 0; } |
Ответ
input.cpp:
1 2 3 4 5 6 7 8 9 |
#include <iostream> int getInteger() { std::cout << "Enter an integer: "; int x; std::cin >> x; return x; } |
main.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 |
#include <iostream> int getInteger(); // предварительное объявление функции getInteger() int main() { int x = getInteger(); int y = getInteger(); std::cout << x << " + " << y << " is " << x + y << '\n'; return 0; } |
То есть, при компиляции только проверяется исходный код на соответствие синтаксису с++, а линкинг — связывает не только разные объектные файлы, но и функции (классы, структуры, перечисления) в одном файле?
Начал изучать С++ примерно месяц назад по другому курсу. Простые програмки без организации проекта, просто как файл .cpp
Прочитал текущие темы (22, 23) и задумался, а стоит ли сейчас заострять внимание на проетах, организации многофайловых программ, заголовках…. или достаточно просто знать об этом и при необходимости изучить.
Но всё же сегодня попробовал именно создать проект с подключениями файлов и заголовка (тема 23).
Всё получилось, спасибо огромное за объяснение!
Но вот обратил внимание на пункт №3: Не следует писать следующую строку в main.cpp: #include "add.cpp"
Собственно говоря можно писать (подключать и таким образом), но только если тренируетесь/отрабатываете синтаксис не в проекте, а на изолированных файлах .cpp
Возможно это приведет к привыканию к неправильному стилю программирования?
Но наверное для обучения и если программка простая или тестируете новый синтаксис, то возможно полезно закидывать все интересные получившиеся подпрограммки в отдельный файлик "mylib.cpp" для коллекции и его линковать не через проект, а простым
#include "mylib.cpp"
Так у вас всегда под ругой будет как-бы конспект лекций.
(на самом деле даже не обязательно чтобы файл имел расширение cpp, можно назвать даже .txt хотя в этом и нет смысла)
Всё это писал для того чтобы ещё раз самому себе уяснить различия линкера проекта и директивы #include "имя_файла"
Нашел ещё один важный момент… Либо я не разобрался, но на сколько понял, режим debug возможен только в проекте.
Для отдельного файла-программки отладка не работает?
Два-три файла, это конечно, хорошо. Но приходит время когда человек начинает косится в сторону Гитхаба, а там уже этих файлов сотни, в проектах.
Найти файл с "main()" не составит труда, но остальные сотни, в ручную добавлять нереально. Попробовал добавить сами папки в настройках проекта, VS при компиляции выдала сотни ошибок.
Как "запихнуть" проект СКАЧАННЫЙ (не работа непосредственно с Гитхабом) в ВизуалСтудию. Как пример можно взять "MPC player" (https://github.com/mpc-hc/mpc-hc)
Просто компиляция проекта не интересует, нужно найти определенное "место" в коде, для его изменения.
Возможно, вы уже разобрались, но кому-нибудь другому может пригодится на будущее ответ.
Вообще, тут не всё так просто. Я бы следовал примерно такому алгоритму, когда надо было бы работать с другим репозиторием.
1. Во-первых, надо почитать файл readme.md. Зачастую там есть инструкция, как надо всё установить, чтобы можно было работать с проектом.
2. Если же в readme ничего нет, то стоит поискать файл с расширением *.sln — это файл, который является самым главным, так сказать. Солюшен (не помню, как на русском правильно будет) хранит информацию о проектах (а их может быть несколько внутри одного солюшена) и разную другую важную информацию. Если таких файлов несколько, надо выбирать тот, который максимально похож на название приложения. В случае с mp-hc — он так и называется mp.hc (хотя есть и еще парочка других). Можно кликнуть по нему два раза и Visual Studio сама откроет всё как надо. Когда она всё откроет, вы увидите уже добавленные, структурированные проекты с файлами и можно приступать к работе.
3. Если же солюшенов нет, то возможно на проекте используется какая-либо система автоматизации сборки, например CMake. В таком случае, надо запускать уже эту систему и она вам сгенерирует нужные файлы для работы с проектом, в том числе и солюшен.
Я так понимаю, что при включении в главный файл заголовочный файл библиотеки, которые были в заголовочном файле копируются, что только прибавляет веса главному файлу или все же копирует лишь функции?
Здравствуйте. А если у меня функция getInteger() будет определена в 2 и более файлах, то как файл с функцией main() поймет какой именно getInteger() мне требуется?
Я методом тыка уже определил что будет ошибка )
Если вы подключите больше одного файла с определением одной и той же функции в проект, то получите ошибку компиляции. Функция определяется 1 раз в 1 файле.
Юрий, у меня проблема, здраствуйте. У меня консоль сразу же закрывается после выполнения задачи, но раньше все было ОК. Как исправить?
Смотрите решение в уроке №7.
Спасибо, Юрий! Замечательные уроки! Получаю удовольствие.. Возникают вопросы.. ищу на сайтах.. нахожу.. Будут посерьёзнее, намерен обращаться, если позволите!
Пожалуйста))
Начал изучать С++ для создания игр на Unreal, но там все как то сложно.
Случайно наткнулся на ваши уроки, это же просто чудо какое то)
Спасибо за ваш труд.
Пожалуй накидаю плюсиков в карму автору)
Мне кажется стоило также указать что хорошим тоном является написать функцию в <name>.cpp, сделать её объявление в <name>.h, после чего подключить <name.h> в основном файле.
Вместо того чтобы писать объявления функций напрямую.
Можете об этом рассказать поподробнее, пожалуйста?
Первый файл с функцией int getInteger():
Второй файл(главный):
Может кто-нибудь знает как это все осуществить при помощи Xcode, или статью какую-нибудь по обучению использования этой среды???
Скачай VisualStudio
А что сложного в Xcode? Всё по аналогии с VS, но со своими особенностями (если уж на то пошло). Можно нагуглить на крайний случай.
Здравствуйте Юрий! Благодарю вас за интересные и познавательные уроки! Стараюсь изучить все тонкости. Вы пожалуйста не бросайте этот сайт. Я думаю что развитие нужно направить в сторону 3D. Как вы на это смотрите?
Пожалуйста)
Здравствуйте я пишу в андроид с помощью Dcoder и хотел пройти тест в конце этого урока но у меня выдаёт ошибку.
/tmp/cc5DeaBV.o: In function
main':
getInteger()'source.cpp:(.text.startup+0x19): undefined reference to
source.cpp:(.text.startup+0x20): undefined reference to `getInteger()'
collect2: error: ld returned 1 exit status
А вот коды
В VS 2019 все компилируется и без #include "pch.h". А если как раз использую #include "pch.h"выдает ошибки "не удается открыть источник файл "pch.h" "и "не удается открыть файл включение: pch.h: No such file or directory " стоит ли пытаться это исправить или оставить как есть?
Сначала разделил верно, потом думал насчет iostream в инпуте, вот только зря добавил в саму функцию инициализацию переменной.
Взглянул в ответ — понял сразу ошибку.
Спасибо за уроки, хороший язык оказывается.
Срочно нужна помощь.
Начал программировать на андроид с приложения Dcoder(c++: GCC compiler 6.3 (знаю, что это неудобно и т. д., но лучше так, чем никак).
С этим уроком получилась зиминка из-за моего не состояния найти способ, как связать несколько файлов именно в этом приложении. Если кто-то что с этим делать, тогда пишите (буду очень благодарен). Автору спасибо за уроки. Всё очень доходчиво написано.
Большое спасибо автору, читаю уроки — не могу оторваться (давно хотел изучить язык С++).
У меня такой вопрос: почему при определении (и описании соответственно) функции getInteger() мы оставили скобки пустыми, (ведь мы задействуем 1 переменную и возвращаем ее значение)?
Дык мы ж не собираемся в энту функцию никаких значений всовывать — вот и параметры в ней нече заявлять!
Все трудности с ошибками прошел, разобрался, помогли подсказки. Осталось не понятным — '\n', что оно означает?
Это символ переноса строки, такая управляющая последовательность(и гуглите 🙂 )
Какая же чудесная находка для меня эти уроки) Автору огромное спасибо за проделанную работу за перевод и адаптацию
Пожалуйста 🙂
Здравствуйте. у меня возникла небольшая заминка. Не могли бы вы помочь мне в её разрешении?
Я создал оба файла, как объяснялось в задании, добавил add.cpp (Добавить новый элемент —> файл.cpp). И в общем всё работает, но в файле add.cpp возникла ошибка: "IntelliSense: не удаётся открыть источник файл "stdafx.h" ".
Помогите разобраться.
Если у вас Visual Studio 2017, то вам нужно #include <pch.h> вместо stdafx.h.
Здравствуйте. Как добавлять файлы в Visual Studio 2017 если он на русском. Просто не очень понятно, переводчик не помогает.
не получается разделить функцию main() на 2 файла (без возвратных функций)…это вообще возможно?)
Данила, функция это тот же блок, т.е. некий цельный кусок, который из своих недр выдаёт на гора некое одно значение, например — превращает в число результат неких математических вычислений. Это значение можно впихнуть в другой блок, который, используя его произведёт также одно значение, но смысл последнего м.б. другим, например — выведется какая-нибудь надпись. Смыслы закладывает кодер (как и всё остальное). А функцию main() — вообще воспринимай как оглавление книги, в котором записаны вызовы всех функций, которые, собственно, и выполняют поставленные кодером задачи.
После беглого просмотра десятка-другого уроков понял, что уроки-то неплохие (не считая некоторых грамматических ошибок), да вот возникло стойкое чувство deja vu. И правда, как оказалось, этот курс — просто перевод с минимальными изменениями курса, представленного на сайте LearnCpp.com.
И хотя я искренне считаю, что людям, не владеющим английским языком, в программировании категорически делать нечего, все же ничего не имею против переводов хороших источников.
Но только если автор честно пишет, что это не его детище, а просто перевод. Я не смог найти никакого упоминания или ссылки на оригинал. Мало того, вы, Юрий, еще и пытаетесь денег на этом заработать, продавая книжку с этим добром. Если я ошибся, прошу прощения, но если нет, то это очень грустно.
А где же, Александр, я писал, что это моё детище? Моё детище — это сайт Ravesli, а уроки С++ — это перевод. На странице "Уроки С++" пролистайте до конца — там источник уроков.
Насчет денег — да, пытаюсь. Только перевод, а не оригинал, да ещё и с "минимальными изменениями". Уроки по С++ останутся бесплатными на сайте до тех пор, пока я буду поддерживать этот сайт, а всё что дополнительно — то это уже на моё усмотрение.
Тогда я ошибся, и это чудесно =) Прошу прощения. Я не заметил ссылки, привык их искать где-то в аннотации.
Ничего, без проблем 🙂
Добрый день! При попытке добавить в функцию, которая находится в отдельном файле, "stdafx.h" компилятор выдаёт ошибку: не удаётся открыть файл включения stdafx.h: No such file or directory. Без stdafx.h всё нормально работает. Visual Studio 2015.
Скорее всего у вас отключены предварительно скомпилированные заголовки, коим и является stdafx.h. Работает без него — хорошо, работает с ним — еще лучше.
Сегодня читала статью с телефона. Очень удобный сайт)
Можно и с телефона, и с планшета, и с компьютера, и с ноутбука — лишь бы желание было, а возможность предоставим 🙂
Когда пишу файл add.cpp постоянно не идёт,это ж линкер мешает по моему???И что ему не так,когда твою программу скопировал,также всё:
Ошибка LNK2019 ссылка на неразрешенный внешний символ _main в функции "int __cdecl invoke_main(void)" (?invoke_main@@YAHXZ) add.cpp 1
Вы сделали всё как в уроке? Правильно добавили файл? К тому проекту, что нужно?
В уроке есть отдельный пункт, где рассказываются возможные ошибки — ваша ошибка — это та же, что и в уроке. Там есть возможные решения.
Почему в VS нужно обязательно подключать библиотеку stdafx.h? Раньше, когда я учился по видеоурокам, я писал программы и без ее подключения. Но конечно есть одно но, я тогда немного изменил параметры создания проекта: поставил галочку на "Empty project" и удалил галочку на "Security Development Lifecycle (SDL) checks ". Может быть это как-то повлияло?
stdafx.h — это реализация механизма «предварительно скомпилированных заголовков», который используется для ускорения процесса сборки проектов. В этом файле содержатся вызовы других библиотек и заголовочных файлов, которые нужны для корректного выполнения ваших программ. Т.е. чтобы не прописывать дополнительно подключение определенных библиотек и заголовочных файлов — эти вызовы записали в одном файле и подключают только stdafx.h (1 строчка кода), а не прописывают дополнительно 20 строчек #include. Но от этого можно увидеть толк и почувствовать реальную пользу, когда ваши проекты состоят минимум из десятка файлов.
Вы можете отключить использование предварительно скомпилированных заголовков в Visual-е в настройках и тогда вам не нужно будет прописывать в каждом файле строчку с подключением stdafx.h. Если вы будете писать простенькие программы на один-два файла, то вам что с stdafx.h, что без него — разница небольшая. Если же будете писать сложные проекты с десятками файлов, то отключив использование stdafx.h, вам придется вручную дополнительно прописывать в каждом файле подключение требуемой библиотеки или заголовочного файла, вызовы которых до этого находились в файле stdafx.h.
Отключается stdafx.h в Visual Studio так: Project (Проект) -> Properties (Свойства) -> C/C++ -> Precompiled Headers (Предварительно скомпилированные заголовки) и выбираете пункт «Not using Precompiled Files» (сокр. «Не использовать»).
По поводу того, как повлияли галочки возле «Empty Project» и «Security Development Lifecycle checks» на отключение механизма «предварительно скомпилированных заголовков» я ничего не могу сказать.
Зачем писать названия в VS (например Source Files) на английском, если в VS есть русский язык?
Это уже дело вкуса, кому как привычнее.
Я имел в виду, что кому то может быть непонятно что значит то или иное название. Мне,как новичку, например, пришлось прибегнуть к гугл-переводчику, чтобы узнать какие это файлы source
Хотите вы этого или нет, но английский, даже самый базовый, знать вам придется. Любые команды, ключевые слова и прочее в коде — это обычные английские слова, которые имеют определенные значения. Писать транслитом (русские слова английской раскладкой) названия файлов, переменных, функций, классов — это, как минимум, некрасиво и неудобно. Что тут делать? Учить английский.
+)).. тоже в гугл лез)))
Согласен, спасибо
Понимаю, полным новичкам могут быть непонятны некоторые слова, но от этого никуда не деться. Там, где можно упростить — я упрощаю, что можно перевести — я перевожу, но всё перевести нельзя. Сам синтаксис любого языка программирования — это английский. И тут уже нужно подстраиваться.