为什么要定义函数而不是包含文件

时间:2012-10-12 11:51:39

标签: c function include

我正在创建一个代码,我在那里有5个包含文件,我在这个文件中定义函数,然后意识到为什么我不应该为所有函数单独header files然后将它们包括在内最后一个文件。但是,我已经看到通常不会这样做。为什么不?这样做有什么特别的缺点吗?

3 个答案:

答案 0 :(得分:3)

这不是一个真正的答案,因为这个问题有一个错误的假设:

  

但是,我已经看到通常不会这样做。

事实并非如此。这是一种常见的做法。一个很好的例子是ffmpeg.h。标题是扩展库的前端。

长编译时间的论点是假的。今天系统非常快。这对于真正庞大的系统来说非常重要,但我真的不认为你使用它们。我自己从未遇到过这样的系统。

编译时间不是执行时间。这是另一种误解。

为方便起见,ffmpeg.h的整个代码:

#ifndef _INCLUDE_FFMPEG_H_
#define _INCLUDE_FFMPEG_H_

#ifdef HAVE_FFMPEG
#include <avformat.h>
#endif

#include <stdio.h>
#include <stdarg.h>

/* Define a codec name/identifier for timelapse videos, so that we can
 * differentiate between normal mpeg1 videos and timelapse videos.
 */
#define TIMELAPSE_CODEC "mpeg1_tl"

struct ffmpeg {
#ifdef HAVE_FFMPEG
    AVFormatContext *oc;
    AVStream *video_st;
    AVCodecContext *c;

    AVFrame *picture;       /* contains default image pointers */
    uint8_t *video_outbuf;
    int video_outbuf_size;

    void *udata;            /* U & V planes for greyscale images */
    int vbr;                /* variable bitrate setting */
    char codec[20];         /* codec name */
#else
    int dummy;
#endif
};

/* Initialize FFmpeg stuff. Needs to be called before ffmpeg_open. */
void ffmpeg_init(void);

/* Open an mpeg file. This is a generic interface for opening either an mpeg1 or
 * an mpeg4 video. If non-standard mpeg1 isn't supported (FFmpeg build > 4680), 
 * calling this function with "mpeg1" as codec results in an error. To create a
 * timelapse video, use TIMELAPSE_CODEC as codec name.
 */
struct ffmpeg *ffmpeg_open(
    char *ffmpeg_video_codec, 
    char *filename, 
    unsigned char *y,    /* YUV420 Y plane */
    unsigned char *u,    /* YUV420 U plane */
    unsigned char *v,    /* YUV420 V plane */
    int width,
    int height, 
    int rate,            /* framerate, fps */
    int bps,             /* bitrate; bits per second */
    int vbr              /* variable bitrate */
    );

/* Puts the image pointed to by the picture member of struct ffmpeg. */
void ffmpeg_put_image(struct ffmpeg *);

/* Puts the image defined by u, y and v (YUV420 format). */
void ffmpeg_put_other_image(
    struct ffmpeg *ffmpeg, 
    unsigned char *y, 
    unsigned char *u, 
    unsigned char *v
    );

/* Closes the mpeg file. */
void ffmpeg_close(struct ffmpeg *);

/*Deinterlace the image. */
void ffmpeg_deinterlace(unsigned char *, int, int);

/*Setup an avcodec log handler. */
void ffmpeg_avcodec_log(void *, int, const char *, va_list);

#endif /* _INCLUDE_FFMPEG_H_ */

答案 1 :(得分:2)

有些人认为将函数放在一个单独的文件中并通过标题包含它们会给项目增加一些开销并增加编译(而不是执行)时间。虽然严格来说这是事实,但在实践中,增加的编译时间可以忽略不计。

我对这个问题的看法更多地基于功能的目的。我反对为每个文件添加一个具有关联标题的函数,因为这很快就变得很难维护。我不认为将所有人放在一个文件中也是一种很好的方法(尽管出于不同的原因,维护也很糟糕)。

我的观点是理想的权衡是看功能的目的。您应该问自己这些功能是否可以在其他地方使用。换句话说,这些功能可以用作其他程序中一系列常见任务的库吗?如果是,则应将这些功能分组到单个文件中。使用与一般任务一样多的文件。例如,所有函数在一个文件中执行数值积分,所有函数在另一个文件中处理文件i / o,所有函数都处理第三个文件中的字符串,依此类推。通过这种方式,您的库是一致的。

最后,我会将一个只对特定程序有意义的任务放入main函数的同一个文件中。例如,任何用于初始化一系列变量的函数。

但最重要的是,你应该采取任何建议作为建议。在一天结束时,您应采用使您(或您的团队)开发最有效率的方法。

答案 2 :(得分:1)

如果您正在编写某种API或库,并且希望该库的用户可以轻松访问其中的函数,那么您应该只创建一个“super-include-header”。 Windows操作系统API就是最明显的例子,其中一个#include可以访问成千上万的函数。

但即使在编写此类库时,您也应该警惕“超级标题”。您应该尝试避免它们的原因与程序设计有关。面向对象的设计要求你应该努力制作孤立的,自主的模块,专注于自己的任务,而不必了解或关心程序的其余部分。

该设计规则背后的基本原理是减少称为coupling 的现象,其中每个模块都严重依赖于其他模块。计算机科学研究(例如like this study)表明,紧密耦合与复杂性相结合会导致更多的软件错误,也会导致更严重的错误。如果具有紧耦合的程序在一个模块中出现错误,则该错误可能会在整个程序中升级并导致灾难。虽然松散耦合的一个自治模块中的错误只会导致特定模块失败。

每次包含头文件时,都会在程序和头文件之间创建依赖关系。因此,虽然只想制作一个包含所有内容的文件很有诱惑力,但您应该避免这种情况,因为它会在项目中的所有模块之间创建紧密耦合。它还会将模块暴露给彼此的全局名称空间,可能导致更多的名称空间冲突与相同的变量名称等。

除了安全问题之外,紧密耦合也会在链接/构建程序时非常烦人。如果紧密耦合,如果没有链接的GUI库,那么数据库模块突然无法工作。