Обычная практика в C ++ — разделять объявления в .h
(или же .hpp
) и внедрение в .cpp
,
Я знаю о двух основных причинах (возможно, есть и другие):
make
из предварительно скомпилированного .o
файлы)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
Тем не менее, я нахожу это часто очень неудобным
особенно, когда вы сильно меняете код (например, на начальном этапе разработки, когда вы еще не уверены в общей структуре всего проекта).
.cpp
а также .h
часть кода при внесении значительных изменений в структуру класса. QuadraticFunc::
) дважды, где вы можете сделать много опечаток и несоответствий, так что компилятор постоянно жалуется (я делаю такие ошибки очень часто)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
) что мне гораздо удобнее?
Однако теперь, когда я начинаю писать более сложные программы, время компиляции начинает быть довольно долгим, когда мне приходится все время перекомпилировать.
Одним из лучших моментов в разделении заголовка и файла класса является то, что вы не надо все компилировать.
Если у вас есть class1.h, class1.cpp, class2.h, class2.cpp, …, classN.h и classN.cpp, эти заголовки включаются только в скомпилированные объекты каждого класса. Поэтому, если логика вашей функции изменяется в class2, а ваш заголовок — нет, вам нужно только скомпилировать class2 в объектный файл. Тогда вы будете делать соединение для всех объектных файлов, которые производят ваш фактический исполняемый файл. Связывание БЫСТРО.
Если вы создаете большие и сложные программы и считаете, что редактирование заголовков является проблемой, подумайте о ДИЗАЙНЕ вашего приложения перед его написанием.
Краткий ответ: нет.
Длинный ответ: до сих пор нет.
Либо вы помещаете весь свой код в файл заголовка, либо используете два файла, где заголовок включен, а исходный файл скомпилирован самостоятельно.
У меня лично нет проблем с использованием двух файлов. Большинство редакторов поддерживают «просмотр двух файлов» — и большинство из них также поддерживают «переход к определению».
Помещение всех функций в объявление класса также имеет другой побочный эффект, заключающийся в том, что все функции помечены как встроенные, что может привести к многократному созданию функции в выходных двоичных файлах, если один и тот же заголовок включен в несколько исходных файлов.
Хотя время компиляции, по моему опыту, является не следствием синтаксического анализа, а частью компиляции кода (обычно это файл .cpp), поэтому, если вы включите несколько больших заголовков, скорее всего, это не будет иметь большого значения ,
Конечно использовать make
[или что-то подобное] с правильно определенными зависимостями для построения вашего проекта.
C ++ — это C ++, а Java — это Java. Разделение вашего исходного кода в файлах .h- и .cpp является частью концепции языка C ++. Если вам не нравится это, вы не должны использовать это.
Помещение всего в один заголовочный файл практически равнозначно включению файла .cpp (который работает, но высоко неприемлемое).
Вам следует не делать это когда:
Примеры: WindowsAPI (COM !!), SFML, Boost (частично) (и много Больше)
Вы можете сделать это, когда:
Примеры: Повышение (частично)
Вы должны сделать это, когда:
Примеры: STL (стандартная библиотека шаблонов C ++)
Прежде всего вы должны написать реализацию и определение в отдельном файле из-за читаемости. Их можно поместить в один файл, но они не читаются. При написании кода напишите для следующего участника, который должен будет понять ваш код. Это важно, потому что следующим может быть вы 🙂 Второй вопрос — это makefile. Сделать это сделать процесс компиляции проще, а не быстрее. Поэтому, если вы вносите изменения в любой из ваших файлов, вам не нужно вносить изменения и вносить файлы. Благодаря make, вам не нужно компилировать файл по порядку снова и снова. Пишите файлы один раз, используйте каждый раз. Но если вы добавляете новый файл кода, который влияет на процесс компиляции, вы должны внести изменения в make-файл. Вы можете получить больше информации о читабельности, написании определения и реализации в отдельных файлах и обо всех других проблемах стиля в руководстве по стилю google c ++ и о makefile в gnu make manuel.