makefile — (избегайте) разбиения кода на .cpp и .h в C ++ и эффективной компиляции

Обычная практика в C ++ — разделять объявления в .h (или же .hpp) и внедрение в .cpp,

Я знаю о двух основных причинах (возможно, есть и другие):

  1. Скорость компиляции (вам не нужно перекомпилировать все, когда вы изменяете только один файл, вы можете связать его make из предварительно скомпилированного .o файлы)
  2. Предварительные декларации иногда необходимы (при реализации class A зависит от class B и реализация class B на class A ) … но у меня нет этой проблемы так часто и обычно я могу ее решить.

В случае объектно-ориентированного программирования это выглядит так:

QuadraticFunction.h:

class QuadraticFunc{
public:
double a,b,c;
double eval ( double x );
double solve( double y, double &x1, double &x2 );
};

QuadraticFunction.cpp:

#include <math.h>
#include "QuadraticFunc.h"
double QuadraticFunc::eval ( double x ){ return c + x * (b + x * a ); };

double QuadraticFunc::solve( double y, double &x1, double &x2 ){
double c_ = c - y;
double D2 = b * b - 4 * a * c_;
if( D2 > 0 ){
double D = sqrt( D2 );
double frac = 0.5/a;
x1 = (-b-D)*frac;
x2 = (-b+D)*frac;
}else{  x1 = NAN; x2 = NAN; }
};

main.cpp :

#include <math.h>
#include <stdio.h>

#include "QuadraticFunc.h"
QuadraticFunc * myFunc;

int main( int argc, char* args[] ){

myFunc = new QuadraticFunc();
myFunc->a = 1.0d; myFunc->b = -1.0d; myFunc->c = -1.0d;

double x1,x2;
myFunc->solve( 10.0d, x1, x2 );
printf( "soulution %20.10f %20.10f \n", x1, x2 );

double y1,y2;
y1 = myFunc->eval( x1 );
y2 = myFunc->eval( x2 );
printf( "check     %20.10f %20.10f \n", y1, y2 );

delete myFunc;
}

затем скомпилируйте его с makefile как это:

FLAGS  = -std=c++11 -Og -g -w
SRCS   = QuadraticFunc.cpp main.cpp
OBJS   = $(subst .cpp,.o,$(SRCS))

all: $(OBJS)
g++ $(OBJS) $(LFLAGS) -o program.x

main.o: main.cpp QuadraticFunc.h
g++ $(LFLAGS) -c main.cpp

QuadraticFunc.o: QuadraticFunc.cpp QuadraticFunc.h
g++ $(LFLAGS) -c QuadraticFunc.cpp

clean:
rm -f *.o *.x

Тем не менее, я нахожу это часто очень неудобным

особенно, когда вы сильно меняете код (например, на начальном этапе разработки, когда вы еще не уверены в общей структуре всего проекта).

  1. Вы должны идти вперед и назад все время между .cpp а также .h часть кода при внесении значительных изменений в структуру класса.
  2. У вас в два раза больше файлов в редакторе и в папке проекта, что сбивает с толку.
  3. Вы должны написать некоторую информацию (например, заголовки функций или QuadraticFunc:: ) дважды, где вы можете сделать много опечаток и несоответствий, так что компилятор постоянно жалуется (я делаю такие ошибки очень часто)
  4. Каждый раз, когда вы добавляете / удаляете / переименовываете какой-то класс, вы должны редактировать Makefileгде вы делаете много других ошибок, которые трудно отследить по выходным данным компилятора (например, я часто забывал написать Makefile, чтобы код перекомпилировал каждую зависимость, которую я редактирую)

С этой точки зрения мне больше нравится, как работает Java. По этой причине я писал свои программы на C ++, просто помещая весь код (включая реализацию) внутрь .h, Как это:

#include <math.h>
class QuadraticFunc{
public:
double a,b,c;
double eval ( double x ){ return c + x * (b + x * a ); }
double solve( double y, double &x1, double &x2 ){
double c_ = c - y;
double D2 = b * b - 4 * a * c_;
if( D2 > 0 ){
double D = sqrt( D2 );
double frac = 0.5/a;
x1 = (-b-D)*frac;
x2 = (-b+D)*frac;
}else{  x1 = NAN; x2 = NAN; }
};
};

с универсальным стандартным make-файлом, например:

FLAGS  = -std=c++11 -Og -g -w

all : $(OBJS)
g++ main.cpp $(LFLAGS) -w -o program.x

( main.cpp остается такой же )

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

Есть ли способ, как использовать преимущества make (быстрее время компиляции) и до сих пор организовать структуру программы в стиле Java (все в теле класса вместо отдельного .h а также .cpp ) что мне гораздо удобнее?

1

Решение

Однако теперь, когда я начинаю писать более сложные программы, время компиляции начинает быть довольно долгим, когда мне приходится все время перекомпилировать.

Одним из лучших моментов в разделении заголовка и файла класса является то, что вы не надо все компилировать.

Если у вас есть class1.h, class1.cpp, class2.h, class2.cpp, …, classN.h и classN.cpp, эти заголовки включаются только в скомпилированные объекты каждого класса. Поэтому, если логика вашей функции изменяется в class2, а ваш заголовок — нет, вам нужно только скомпилировать class2 в объектный файл. Тогда вы будете делать соединение для всех объектных файлов, которые производят ваш фактический исполняемый файл. Связывание БЫСТРО.

Если вы создаете большие и сложные программы и считаете, что редактирование заголовков является проблемой, подумайте о ДИЗАЙНЕ вашего приложения перед его написанием.

4

Другие решения

Краткий ответ: нет.

Длинный ответ: до сих пор нет.

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

У меня лично нет проблем с использованием двух файлов. Большинство редакторов поддерживают «просмотр двух файлов» — и большинство из них также поддерживают «переход к определению».

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

Хотя время компиляции, по моему опыту, является не следствием синтаксического анализа, а частью компиляции кода (обычно это файл .cpp), поэтому, если вы включите несколько больших заголовков, скорее всего, это не будет иметь большого значения ,

Конечно использовать make [или что-то подобное] с правильно определенными зависимостями для построения вашего проекта.

1

C ++ — это C ++, а Java — это Java. Разделение вашего исходного кода в файлах .h- и .cpp является частью концепции языка C ++. Если вам не нравится это, вы не должны использовать это.

Помещение всего в один заголовочный файл практически равнозначно включению файла .cpp (который работает, но высоко неприемлемое).
Вам следует не делать это когда:

  • написание стандартных классов, функций, …
  • многократное использование раздела кода в больших программах (приводит к ошибке переопределения, когда вы все включаете в main.cpp в противном случае)
  • Вы хотите передать часть вашей программы в статическую / динамическую библиотеку. Практически каждая созданная доступная библиотека работает таким образом.

Примеры: WindowsAPI (COM !!), SFML, Boost (частично) (и много Больше)

Вы можете сделать это, когда:

  • код делает очень простые вещи, например Операция сдвига битов (создание цветовых кодов), анализ строк, …

Примеры: Повышение (частично)

Вы должны сделать это, когда:

  • Создание шаблонных классов или функций по мере их создания во время компиляции. Это один из основных и наиболее обсуждаемых недостатков концепции .h / .ccp, так что вы не первый, кто задумывается об этом.

Примеры: STL (стандартная библиотека шаблонов C ++)

1

Прежде всего вы должны написать реализацию и определение в отдельном файле из-за читаемости. Их можно поместить в один файл, но они не читаются. При написании кода напишите для следующего участника, который должен будет понять ваш код. Это важно, потому что следующим может быть вы 🙂 Второй вопрос — это makefile. Сделать это сделать процесс компиляции проще, а не быстрее. Поэтому, если вы вносите изменения в любой из ваших файлов, вам не нужно вносить изменения и вносить файлы. Благодаря make, вам не нужно компилировать файл по порядку снова и снова. Пишите файлы один раз, используйте каждый раз. Но если вы добавляете новый файл кода, который влияет на процесс компиляции, вы должны внести изменения в make-файл. Вы можете получить больше информации о читабельности, написании определения и реализации в отдельных файлах и обо всех других проблемах стиля в руководстве по стилю google c ++ и о makefile в gnu make manuel.

0
По вопросам рекламы [email protected]