Подтвердить что ты не робот

Компиляция и объединение нескольких файлов в С++

Один из моих "не-программистов" недавно решил сделать программу на С++ для решения сложной механической проблемы.

Он написал каждую функцию в отдельном файле .cpp, а затем включил их все в основной исходный файл, что-то вроде этого:

main.cpp:

#include "function1.cpp"
#include "function2.cpp"
...
int main() 
{
...

}

Затем он скомпилировал код с одной строкой gcc:

g++ main.cpp    // took about 2 seconds 

Теперь я знаю, что это должно работать, но я не уверен, является ли включение .cpp файлов непосредственно в основную программу хорошей идеей. Я видел следующую схему несколько раз, где все прототипы функций входят в заголовочный файл с ключевым словом extern, например:

funcs.h:

extern void function1(..);
extern void function2(..);
...

main.cpp:

...
#include "funcs.h"
...

& компиляция с помощью:

g++ -c function1.cpp
g++ -c function2.cpp
...
g++ -c main.cpp
g++ -o final main.o function1.o function2.o ...

Я думаю, что эта схема лучше (с makefile, конечно). Какие причины я могу дать моему другу, чтобы убедить его в этом?

4b9b3361

Ответ 1

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

Если проект настолько мал, что его можно скомпилировать за 2 секунды, то традиционный подход не принесет значительного эффекта, хотя он делает то, что, как ожидается, может сэкономить время разработчика - например, ваше и наше:-). Балансирование того, что поддержка make файла также требует времени, хотя вы вполне можете это сделать, чтобы в любом случае удобно записывать каталоги, библиотеки, компиляторы и т.д.

Фактические последствия для написанного/сгенерированного кода:

  • Файлы cpp обычно сначала включают свои собственные заголовки, что обеспечивает проверку работоспособности, что содержимое заголовка может использоваться независимо другим кодом клиента: объединить все вместе, а пространство имен уже "загрязнено", включая предыдущие файлы заголовков/реализации
  • компилятор может оптимизироваться лучше, когда все находится в одной единицы перевода (+1 для комментария leppie, сделайте то же самое...)
  • статические не-членные переменные и анонимные пространства имен являются закрытыми для единицы перевода, поэтому включение нескольких cpps означает их совместное использование для улучшения или хуже (+1 для Alexander: -))
  • скажем, что файлы cpp определяют функцию или переменную, которая не упоминается в ее заголовке, и может даже быть в анонимном пространстве имен или статическом: код позже в блоке перевода мог бы свободно звонить без необходимости взломать собственную декларацию ( это плохо - если функция должна была вызываться за пределами своего собственного cpp, тогда она должна была быть в заголовке и внешнем экспонале в его объекте единицы перевода).

BTW - на С++ ваши заголовки могут объявлять функции без явного использования ключевого слова extern, и это нормально для этого.

Ответ 2

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

В среде IDE также проще связывать все .cpp файлы (например, MSVC).