Компиляция и компоновка нескольких файлов в C ++ - PullRequest
20 голосов
/ 29 сентября 2010

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

Он написал каждую функцию в отдельном файле .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, конечно).Какие причины я могу дать своему другу, чтобы убедить его в этом?

Ответы [ 2 ]

21 голосов
/ 29 сентября 2010

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

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

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

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

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

6 голосов
/ 29 сентября 2010

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

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

...