2.7 – Программы с несколькими файлами исходного кода

Добавлено 10 апреля 2021 в 22:25

Добавление файлов в ваш проект

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

Лучшая практика


Когда вы добавляете в проект новые файлы исходного кода, давайте им расширение .cpp.

Для пользователей Visual Studio

В Visual Studio кликните правой кнопкой мыши на папке Исходные файлы (Source Files) в окне Обозревателя решений (Solution Explorer) и выберите Добавить (Add) → Создать элемент… (New Item…).

Рисунок 1 Добавление нового файла в проект в Visual Studio
Рисунок 1 – Добавление нового файла в проект в Visual Studio

Убедитесь, что у вас выбран Файл C++ (.cpp). Дайте новому файлу имя, и он будет добавлен в ваш проект.

Рисунок 2 Создание нового файла в проекте в Visual Studio
Рисунок 2 – Создание нового файла в проекте в Visual Studio

Примечание. Если вы создаете новый файл из меню Файл (File), а не из своего проекта в обозревателе решений, новый файл не будет добавлен в ваш проект автоматически. Вам придется добавить его в проект вручную. Для этого кликните правой кнопкой мыши на папке Исходные файлы (Source Files) в окне Обозревателя решений (Solution Explorer) и выберите Добавить (Add) → Существующий элемент (Existing Item), а затем выберите свой файл.

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

Для пользователей Code::Blocks

В Code::Blocks перейдите в меню File (Файл) и выберите New (Создать) → File… (Файл…).

Рисунок 3 Создание нового файла в Code::Blocks
Рисунок 3 – Создание нового файла в Code::Blocks

В диалоговом окне New from template (Создать из шаблона) выберите C/C++ source (Исходный файл C/C++) и нажмите Go (Перейти).

Рисунок 4 Создание нового исходного файла C/C++ в Code::Blocks
Рисунок 4 – Создание нового исходного файла C/C++ в Code::Blocks

На этом этапе вы можете увидеть или не увидеть приветствие в диалоговом окне мастера создания исходного файла C/C++. Если да, щелкните Next (Далее).

Рисунок 5 Диалоговое окно мастера создания исходного файла C/C++
Рисунок 5 – Диалоговое окно мастера создания исходного файла C/C++

На следующей странице мастера выберите C++ и нажмите Next (Далее).

Рисунок 6 Выбор языка при создании нового исходного файла в Code::Blocks
Рисунок 6 – Выбор языка при создании нового исходного файла в Code::Blocks

Теперь дайте новому файлу имя (не забудьте расширение .cpp) и выберите все конфигурации сборки. Наконец, выберите Finish (Готово).

Рисунок 7 Указание имени файла и выбор конфигураций сборки
Рисунок 7 – Указание имени файла и выбор конфигураций сборки

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

Для пользователей GCC/G++

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

g++ main.cpp add.cpp -o main

где main.cpp и add.cpp – это имена ваших исходных файлов, а main – имя выходного файла.

Пример с несколькими файлами

В уроке «2.6 – Предварительные объявления и определения» мы рассмотрели программу с одним исходным файлом, которая не компилируется:

#include <iostream>
 
int main()
{
    std::cout << "The sum of 3 and 4 is: " << add(3, 4) << '\n';
    return 0;
}
 
int add(int x, int y)
{
    return x + y;
}

Когда компилятор достигает вызова функции add в строке 5 в функции main, он не знает, что такое add, потому что мы определили add только в строке 9! Нашим решением было либо переупорядочить функции (поместив сначала add), либо использовать для add предварительное объявление.

Теперь посмотрим на аналогичную программу из нескольких исходных файлов:

add.cpp:

int add(int x, int y)
{
    return x + y;
}

main.cpp:

#include <iostream>
 
int main()
{
    std::cout << "The sum of 3 and 4 is: " << add(3, 4) << '\n'; // ошибка компиляции
    return 0;
}

Ваш компилятор может решить сначала скомпилировать либо 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 <iostream>
 
// необходимо, чтобы main.cpp знал, что add() - это функция, объявленная в другом месте
int add(int x, int y);
 
int main()
{
    std::cout << "The sum of 3 and 4 is: " << add(3, 4) << '\n';
    return 0;
}

add.cpp (остается прежним):

int add(int x, int y)
{
    return x + y;
}

Теперь, когда компилятор компилирует main.cpp, он будет знать, что такое идентификатор add, и будет удовлетворен. Линкер соединит вызов функции add в main.cpp с определением функции add в add.cpp.

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

Попробуйте скомпилировать add.cpp и main.cpp с предварительным объявлением. Если вы получили ошибку линкера, убедитесь, что вы правильно добавили add.cpp в свой проект или строку компиляции.

Что-то пошло не так!

Есть много вещей, которые могут пойти не так, когда вы в первый раз попытаетесь работать с несколькими файлами. Если вы попробовали приведенный выше пример и столкнулись с ошибкой, проверьте следующее:

1. Если вы получаете ошибку компилятора о том, что add не определена в main, вы, вероятно, забыли предварительное объявление для функции add в main.cpp.

2. Если вы получаете сообщение об ошибке компоновщика о том, что add не определена, например

unresolved external symbol "int __cdecl add(int,int)" (?add@@YAHHH@Z) referenced in function _main

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 вместо того, чтобы рассматривать их как отдельные файлы.

Резюме

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

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

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

Небольшой тест

Вопрос 1

Разделите следующую программу на два файла (main.cpp и input.cpp). main.cpp должен содержать функцию main, а input.cpp должен содержать функцию getInteger.

Не забывайте, что вам понадобится предварительное объявление в main.cpp для функции getInteger().

#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:

#include <iostream> // нам нужен iostream, поскольку мы используем его в этом файле
 
int getInteger()
{
	std::cout << "Enter an integer: ";
	int x{};
	std::cin >> x;
	return x;
}

main.cpp:

#include <iostream> // здесь нам тоже нужен iostream, поскольку мы используем его и в этом файле
 
int getInteger(); // предварительное объявление функции getInteger
 
int main()
{
	int x{ getInteger() };
	int y{ getInteger() };
 
	std::cout << x << " + " << y << " is " << x + y << '\n';
	return 0;
}

Теги

C++ / CppCode::BlocksIDEVisual StudioДля начинающихКомпиляторЛинкерОбучениеПрограммирование

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.