C++中头文件应该包含什么内容?

6
我刚开始学习C++,这是我第一个程序。这个程序包含3个不同的文件:头文件(prog1.h),一个prog1.cpp文件(对于这个我还不确定术语)和一个测试文件,它包括main函数来测试我们的程序(prog1_test.cpp)。
我并不需要在这方面得到帮助(当然,我肯定会在进一步使用中再次提问),但是我认为您需要知道程序应该做什么,才能理解我的问题。我们的程序应该从文件中读取一些数字,并将这些数字放入二维数组中。然后,它应该为每个数字值分配字符,并打印当前数组以及使用字符创建的"图片"。接下来,程序将遍历数组,确保每个数字与其相邻值之间的差异不超过1,如果超过了,则用其相邻值的平均值替换该数字。最后,程序将打印出这个被更正后的数组和由字符组成的"图片"。
我知道头文件应该包含将在多个文件中使用的代码,但我不确定我的程序中哪些部分应该放在这个文件中。例如,我的代码来打开、读取和关闭数据文件是否应该包括在头文件中?还是因为该文件只被打开、读取和关闭一次就不需要呢?

头文件不应包含将被多个文件中的其他代码调用的代码,它应该包括其他代码需要了解该代码的内容。这基本上意味着函数和类声明。如果您给我们一些简单的代码示例,我们可以更详细地解释。 - Beta
那么,这可能包括将字符分配给数字的函数以及检查错误(数字与其相邻数字相差超过1)的函数?我本来想在进行多个文件时将其分解,但我认为在有了可用代码后将其全部放在一个文件中然后再进行分解会更容易。实际上我还没有编写任何代码,就像我说的,我正在学习这些东西,所以仍在努力开始。不过等我有一些代码写好了,我会发布它们的。 - user2932587
在C++(和C)中,#include 相当于复制粘贴所包含文件的内容。这有很多影响,但最重要的是它将帮助您理解链接器何时抱怨重复符号,这无疑会遇到。 - hyde
这个回答解决了你的问题吗?应该把什么放进.h文件中? - Antti Haapala -- Слава Україні
3个回答

5
头文件包含函数和类的声明,它们仅声明函数名称、返回类型和参数列表。.cpp文件包含这些函数的定义 - 即实际实现。如果您在其他文件中#include头文件,则可以看到头文件,但是实现细节隐藏在.cpp文件中。
在.cpp文件中声明/定义的任何内容都不会对程序的其余部分可见,因此您可以在.cpp文件中定义内部变量、辅助函数等,并且那些实现细节将不可见。下面是我示例中的foo()
您的程序的非常粗略的草图如下:
在prog1.h中:
#include <iostream> // and whatever other libraries you need to include

#define ARRAY_SIZE 100 // and other defines

// Function declarations

// Read number from file, return int
void read_number(int array[ARRAY_SIZE][ARRAY_SIZE]);

char assign_char(int n);

void print_array(int array[ARRAY_SIZE][ARRAY_SIZE]);

void neighbor_check(int array[ARRAY_SIZE][ARRAY_SIZE]);

在prog1.cpp文件中:
// included headers, defines, and functions declared in prog1.h are visible
#include "prog1.h"

void read_number(int array[ARRAY_SIZE][ARRAY_SIZE]) {
    // implementation here
}

char assign_char(int n) {
    char c;
    // implementation here
    return c;

}

void print_array(int array[ARRAY_SIZE][ARRAY_SIZE]) {
    // implementation here
}

void neighbor_check(int array[ARRAY_SIZE][ARRAY_SIZE]) {
    // implementation here
}

// not visible to anything that #includes prog1.h
// since it is not declared in prog1.h
void foo() {
    // implementation here
}

在prog1_test.cpp文件中:

// included headers, defines, and functions declared in prog1.h are visible
#include "prog1.h"
// any other includes needed by main()

int main() {
   int array[ARRAY_SIZE][ARRAY_SIZE];

   read_number(array);

   for (int i = 0; i < ARRAY_SIZE; i++) {
       for (int j = 0; j < ARRAY_SIZE; j++) {
           assign_char(array[i][j]);
       }
   }

   neighbor_check(array);

   print_array(array);

   return 0;
}

非常感谢,这非常有帮助! - user2932587

3
使用各种头文件以及与应用程序文件分开的代码/实现文件,旨在促进代码的可维护性和重复使用。通过将所有相关函数、变量和数据结构收集到单个文件中,我们可以避免在每个后续应用程序中复制该代码。
例如,假设您将开发与矩阵或数组相关的例程。此方案使您能够将处理数组的常见例程收集到单个文件中,例如arrays.c。现在,无论您写多少个程序,您都有一个包含数组例程的公共文件。如果您编写了5个需要数组例程的应用程序,您可以在arrays.c中随时使用它们。
为了可维护性,假设您现在有一种更有效的方法来处理从文件到数组的输入。您只需更新arrays.c中的一个read_array函数,而不是在5个不同的应用程序程序文件中返回并更新它,就可以完成更新那些例程以供使用的所有应用程序。
那么header文件如何适应呢?为了在其他应用程序中使用您的数组函数,这些应用程序需要一种通用的方式来包含代码以使其可用于使用。这是通过在需要它的每个应用程序中包含您的arrays.h头文件来完成的。那么arrays.h中放什么,arrays.c中放什么呢?您的头文件将包括header文件、函数声明、数据结构和变量,这些对于实现arrays.c中包含的function definitions是必要的。这样,任何希望使用您的数组例程的应用程序只需在其文件开头包含一行#include "arrays.h",即可访问包含在arrays.c中的数组函数。
以下是一个示例(使用C语言),以帮助澄清问题。假设您有几个数组函数,可以将文本文件中的行读入字符串read_array,打印行prn_array,然后释放数组和字符串所使用的内存free_array。您已将数组函数收集到array.c中。(我们知道我们将在顶部包含array.h。)例如:
#include "array.h"

char **read_array (char *filename)
{
    char *buffer = NULL;
    size_t len = 0;
    ssize_t read;
    char **array = NULL;
    int cnt = 0;

    FILE *fp;
    fp = fopen (filename, "r");      //open file , read only
    if (!fp) {
        fprintf (stderr, "failed to open file for reading\n");
        return NULL;
    }

    array = calloc (AMAX, sizeof (*array) * AMAX); /* allocate pointers, set NULL */

    while ((read = getline (&buffer, &len, fp)) != -1) { /* read each line */

        if (cnt == AMAX) {
            fprintf (stderr, "Error: AMAX reached\n");
            break;
            /* you will realloc *array here in real life */
        }

        if (buffer[read-1] == '\n') {   /* strip newline              */
            buffer[read-1] = 0;
            read -= 1;
        }

        array[cnt] = strdup (buffer);   /* copy buffer to array[cnt]   */
        cnt++;                          /* increment counter           */
    }

    fclose (fp);                        /* close file stream (fp)      */
    return array;
}

void prn_array (char **array)
{
    register int j = 0;
    while (array[j]) {
        printf ("  array[%d]: %s\n", j, array[j]);
        j++;
    }
}

void free_array (char **array)
{
    register int j = 0;
    while (array[j]) {      /* for each allocated string */
        free (array[j]);    /* free pointer to string    */    
        j++;
    }
    free (array);           /* free array                */
}

现在,让我们创建我们的标题。我们收集我们的函数声明以及使用的任何变量或数据结构,并包括 array.h 中的系统头文件和函数所需的任何其他已包含的头文件。
#ifndef MY_ARRAY_H
#define MY_ARRAY_H  1

/* headers required for array.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define AMAX 100

/* function declaration */
char **read_array (char *filename);
void prn_array (char **array);
void free_array (char **array);

#endif  /* MY_ARRAY_H */

注意:我们将头文件的内容包含在一个测试#ifndef MY_ARRAY_H中,这只是询问编译器是否已经定义了MY_ARRAY_H,如果没有定义,则让编译器知道从我们的#define开始直到达到if语句结束前的#endif的所有内容都被定义为MY_ARRAY_H,这样如果另一个文件尝试包含相同的头文件,编译器就知道它已经有了,不需要再次包含它。(不要求,但我们还给出了MY_ARRAY_H的定义值1
那么我们如何利用arrays.c中的自定义头文件array.h和处理数组的自定义函数呢?我们编写一个使用arrays.c中的函数的应用程序,并在应用程序中声明arrays.c中的所有函数,我们只需在应用程序中包含array.h即可。(下面称为application.c
#include <stdio.h>
#include "array.h"                      /* include array.h  */

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

    if (argc < 2) {
        printf ("filename.csv please...\n");
        return 1;
    }

    char **my_array = NULL;             /* declare pointers */

    my_array = read_array (argv[1]);    /* call read_array  */
    prn_array (my_array);               /* call prn_array   */
    free_array (my_array);              /* call free_array  */

    return 0;
}

编译"application.c"和"array.c"文件:
gcc -Wall -Wextra -o application application.c array.c

可以在任何小于100行的文本文件上运行它

$ ./application input.txt
  array[0]: 1.  This is a simple input file with line numbers
  array[1]: 2.  for use with application showing the use of
  array[2]: 3.  header file: array.h
  array[3]: 4.  array functions defined in: array.c
  array[4]: 5.  (blank)
  array[5]: 6.  compiled with the following:
  array[6]: 7.  gcc -Wall -Wextra -o application application.c array.c
  array[7]: 8.  --------
  array[8]: 9.  Now you know!

现在,您可以看到创建一个包含相关函数、变量和数据结构的单独文件以及创建头文件的好处。我们可以通过在应用程序中包含头文件“arrays.h”来在任何应用程序中重复使用该代码。我们可以在一个地方更新和维护所有数组函数,并且更新我们的“arrays.c”将更新所有其他使用该代码的应用程序。
我知道我没有涵盖所有内容,但请确保您对这些概念有所了解。它们是使用C/C++中的多个源文件和包含文件的基本概念。

0

以下是您的头文件中应该包含的基本内容:

  1. 类体,包括函数声明、数据变量、构造函数和析构函数声明。
  2. 类中所有函数的声明。注意:不包括函数体或定义。
  3. 内联函数。
  4. 宏定义。
  5. 所有库包含头文件。因此,您只需要在相应的 .c 文件中添加一个头文件。
  6. 结构体。
  7. ifndef 块。
  8. 如果需要,可以提前声明类。
  9. 为函数、类和变量编写 Doxygen 注释。
  10. 在头文件顶部作为注释描述此文件。
  11. 等等...

头文件不应包含任何代码。


那么这是不是类似于Java中的接口?我在想,因为接口只包含抽象类的声明,而没有任何关于这些声明的代码体,也许头文件有点类似?我真的很新手,在很大程度上是通过谷歌和我的教科书自学的(我的教授并不擅长以我们实际理解他在说什么的方式来解释事情),但我有点熟悉Java,所以试图利用我从中学到的知识来帮助我学习这个,如果这有任何意义的话。 - user2932587
1
你还应该将模板代码放在头文件中。这样做与第三点相矛盾,因此违反了你的最后一条规则。 - Willem Hengeveld
对于cmaster所说的,你是正确的...但是我只在一个头文件和一个cpp文件的上下文中回答了。 - user2641018
1
在C++中,将内联函数放在头文件中是最佳实践。你不能在头文件中声明内联函数并将其主体放在cpp文件中。 - user2641018
1
一旦您使用模板,您无法执行粗体字的备注。 - celtschk
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接