(避免)在C ++和高效编译中将代码拆分为.cpp和.h

时间:2015-12-28 08:29:03

标签: c++ makefile code-organization code-maintainability

C ++中的常见做法是将.h(或.hpp)和实现中的声明分隔为.cpp

我知道两个主要原因(也许还有其他原因):

  1. 汇编速度(当您只更改一个文件时,您不必重新编辑所有内容,您可以通过make从预编译的.o文件中将其链接起来)
  2. 有时需要前瞻性声明(class A的实施取决于class B以及class Bclass A的实施)......但我没有这个问题经常发生,我可以解决它。
  3. 在面向对象编程的情况下,它看起来像这样:

    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所以该代码重新编译我编辑的每个依赖项
    5. 从这个角度来看,我更喜欢Java的工作原理。出于这个原因,我只是通过将所有代码(包括实现)放在.h中来编写我的C ++程序。像这样:

      #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; }
          };
      };
      

      使用通用默认的makefile,如下所示:

      FLAGS  = -std=c++11 -Og -g -w
      
      all : $(OBJS)
          g++ main.cpp $(LFLAGS) -w -o program.x
      

      main.cpp保持不变)

      然而,现在当我开始编写更复杂的程序时,编译时间开始很长,当我不得不一直重新编译所有内容时。

      有没有办法如何利用make (更快的编译时间)的优势,仍然以类似Java的方式组织程序结构(一切都在类身体而不是单独的.h.cpp)哪个更方便?

4 个答案:

答案 0 :(得分:5)

  

然而,现在当我开始编写更复杂的程序时,编译时间开始很长,因为我必须一直重新编译所有内容。

分隔标题和类文件的最佳点之一是必须编译所有内容。

当你有class1.h,class1.cpp,class2.h,class2.cpp,...,classN.h和classN.cpp时,这些头只包含在每个类的编译对象中。因此,如果你的函数的逻辑在class2中发生了变化但你的标题没有,那么你只需要将class2编译成目标文件。然后,您将为生成实际可执行文件的所有目标文件执行链接。链接很快。

如果您正在构建大型复杂程序并发现编辑标题是问题所在,请在编写之前考虑设计您的应用程序。

答案 1 :(得分:1)

简答:不。

答案很长:仍然没有。

要么将所有代码放在头文件中,要么使用两个文件,其中包含标题,源文件自行编译。

我个人对使用两个文件没有任何问题。大多数编辑器支持“双文件视图” - 其中大多数也支持“跳转到定义”。

将所有函数放在类声明中也有另一个副作用,那就是所有函数都标记为内联,这可能导致函数在输出二进制文件中多次生成,如果多个源中包含相同的标题文件。

虽然编译时间,根据我的经验,不是解析的结果,而是编译的代码生成部分 - 通常是.cpp文件 - 所以如果你包含几个大的头文件,很可能不会很重要。

当然使用make [或类似的东西]和正确定义的依赖项来构建项目。

答案 2 :(得分:1)

C ++是C ++,Java是Java。在.h- und .cpp文件中拆分源代码是C ++语言概念的一部分。如果你不喜欢它,你就不应该使用它。

将所有内容放在一个头文件中与包含.cpp文件(可行但高度不合适)的实际情况相同。 您应该执行此操作:

  • 编写标准类,函数,......
  • 在较大的程序中多次使用代码部分(否则在main.cpp中包含所有内容时会导致重新定义错误)
  • 您希望将部分程序外包到静态/动态库中。几乎所有可用的库都以这种方式工作。

示例: WindowsAPI(COM !!),SFML,Boost(部分)(以及很多更多)

你可以在以下时间执行此操作:

  • 代码执行非常简单的事情,例如位移操作(制作颜色代码),字符串分析,......

示例:提升(部分)

您必须在以下时间执行此操作:

  • 创建在编译运行时生成的模板类或函数。这是.h / .ccp概念的主要和讨论最多的缺点之一,所以你不是第一个对此感到疑惑的人。

示例: STL(C ++标准模板库)

答案 3 :(得分:0)

首先,由于可重复性,您必须在分离文件中编写实现和定义。有可能将它们放入同一个文件中,但它不可读。编写代码时,请为需要理解代码的下一个贡献者编写代码。这很重要,因为下一个可能是你:)第二个问题是makefile。 Make是为了简化编译过程而不是更快。因此,如果您在任何文件中进行更改,则无需更改ant make文件。感谢make,你不需要一次又一次地编译你的文件。写一次文件,每次都使用。但是如果添加影响编译过程的新代码文件,是的,你必须在makefile中进行更改。您可以在google c ++样式指南中了解有关可读性,编写定义和实现的更多信息以及所有其他样式问题以及gnu make manuel中的makefile。