C ++头文件,代码分离

时间:2008-11-11 04:46:02

标签: c++ header code-separation

我是C ++的新手,我有一些关于代码分离的一般性问题。我目前在一个文件中构建了一个小应用程序。我现在想要做的是将它转换为单独的文件,使它们包含类似的代码或诸如此类的东西。我现在真正的问题是,我怎么知道如何分开事物?代码应该分开的隐形边际是什么?

另外,头文件有什么意义?它是否转发声明方法和类,以便在编译期间链接器包含它们之前我可以在我的代码中使用它们?

对方法或最佳实践的任何见解都会很棒,谢谢!

4 个答案:

答案 0 :(得分:25)

头文件应包含类和函数声明。

源文件包含类和函数定义。

标准做法(即更容易阅读)每个头文件有一个声明,每个源文件有一个定义,但是对于小的(读取更简单的助手)对象,有时会将它们与相关的更实质的对象分组。

示例:类菜单

Menu.h:     Contains the Menu declaration.
Menu.cpp:   Contains the Menu definition.

头文件包含声明的原因是,您可以从多个源文件中包含它们,因此每个源文件对每个类和函数具有完全相同的定义。

这样考虑:
如果您没有头文件,那么您需要在每个源文件中都有类和/或函数声明(没有)定义,这意味着每个文件中都有相同声明的副本。因此,如果修改类,则需要在每个文件中进行相同的修改。通过使用头文件,您可以在一个地方声明,因此只需要修改一个对象。

答案 1 :(得分:20)

首先,除了需要的文件之外,你不应该把任何东西放到任何其他文件不需要的标题中。然后,让我们在下面定义我们需要的东西。

  

翻译单位

     

翻译单元是正在编译的当前代码,包含所有代码   通过它,直接或间接。一个翻译单元转换为一个.o / .obj文件。

     

程序

     

这就是你所有的.o / .obj文件链接在一起成为一个二进制文件   执行以形成一个过程。

拥有不同翻译单位的要点是什么?

  1. 减少依赖关系,这样,如果更改一个类的一个方法,则不必重新编译程序的所有代码,只需重新编译受影响的翻译单元。一个
  2. 通过翻译单元本地名称减少可能的名称冲突,将其链接在一起时,其他翻译单元无法看到这些名称冲突。
  3. 现在,您如何将代码拆分为不同的翻译单元?答案是没有“所以你这样做!”,但你必须根据具体情况考虑它。通常很清楚,因为你有不同的课程,可以而且应该放在不同的翻译单元中:

    <强> foo.hpp:

    /* Only declaration of class foo we define below. Note that a declaration
     * is not a definition. But a definition is always also a declaration */
    class foo;
    
    /* definition of a class foo. the same class definition can appear 
       in multiple translation units provided that each definition is the same  
       basicially, but only once per translation unit. This too is called the  
       "One Definition Rule" (ODR). */
    class foo {
        /* declaration of a member function doit */
        void doit();
    
        /* definition of an data-member age */
        int age;
    };
    

    声明一些自由函数和对象:

    /* if you have translation unit non-local (with so-called extern linkage)  
       names, you declare them here, so other translation units can include  
       your file "foo.hpp" and use them. */
    void getTheAnswer();
    
    /* to avoid that the following is a definition of a object, you put "extern"  
       in front of it. */
    extern int answerCheat;
    

    <强> foo.cpp:

    /* include the header of it */
    #include "foo.hpp"
    
    /* definition of the member function doit */
    void foo::doit() {
        /* ... */
    }
    
    /* definition of a translation unit local name. preferred way in c++. */
    namespace {
        void help() {
            /* ... */
        }
    }
    
    void getTheAnswer() {
        /* let's call our helper function */
        help();
        /* ... */
    }
    
    /* define answerCheat. non-const objects are translation unit nonlocal  
       by default */
    int answerCheat = 42;
    

    <强> bar.hpp:

    /* so, this is the same as above, just with other classes/files... */
    class bar {
    public:
        bar(); /* constructor */
    }; 
    

    <强> bar.cpp:

    /* we need the foo.hpp file, which declares getTheAnswer() */
    #include "foo.hpp"
    #include "bar.hpp"
    
    bar::bar() {
        /* make use of getTheAnswer() */
        getTheAnswer();
    }
    

    请注意,匿名命名空间(如上所述)中的名称不会发生冲突,因为它们似乎是本地翻译单元。实际上他们不是,他们只是有独特的名字,所以他们不会发生冲突。如果你真的想(没有理由)翻译单元本地名称(例如因为与c的兼容性,所以C代码可以调用你的函数)你可以这样做:

    static void help() { 
        /* .... */
    }
    

    ODR还说你不能在一个程序中拥有任何对象或非内联函数的多个定义(类是类型,而不是对象,所以它不适用于它们)。所以你必须注意不要将非内联函数放入标题中,或者不要放置像“int foo”这样的对象。在标题中。这将导致链接器错误,然后链接器尝试将包括这些头的转换单元链接在一起。

    我希望我能帮助你一点。现在这是一个很长的答案,某些地方确实存在错误。我知道翻译单元严格地以另一种方式定义(预处理器的输出)。但我认为将上述内容纳入其中并不会增加很大的价值,而且会让事情变得混乱。如果你发现了真正的错误,请随时打我!)

答案 2 :(得分:4)

决定如何将代码分成不同的类/函数是编程的主要任务之一。有关如何执行此操作的许多不同指南,我建议您阅读一些有关C ++和面向对象设计的教程以帮助您入门。

一些基本指南将

  • 将所使用的东西放在一起 一起
  • 为域对象创建类 (例如文件,收藏品等)

头文件允许您声明一个类或函数,然后在几个不同的源文件中使用它。例如,如果在头文件中声明一个类

// A.h
class A
{
public:
    int fn();
};

然后,您可以在多个源文件中使用此类:

// A.cpp
#include "A.h"
int A::fn() {/* implementation of fn */}

//B.cpp
#include "A.h"
void OtherFunction() {
    A a;
    a.fn();
}

因此,头文件使您可以将声明与实现分开。如果您将所有内容(声明和实现)放在源文件(例如A.cpp)中,那么尝试将其包含在第二个文件中,例如

// B.cpp
#include  "A.cpp" //DON'T do this!

然后你可以编译B.cpp但是当你试图链接你的程序时,链接器会抱怨你有多个定义的对象 - 这是因为你有多个A实现的副本。

答案 3 :(得分:0)

建议: 1.立即为您的应用准备好设计。 2.根据设计,创建相互交互的必要对象。 3.重构或完全更改现有代码以适应新创建的设计。

头文件提供了可能使用它的其他类的接口。