标题守卫似乎不起作用?

时间:2013-09-02 18:59:17

标签: c++ header-files

我在单独的标题中声明了一些常量变量(即 constant.h )。

我在我的 debug.cpp 中包含了constant.h以访问变量。

我在 main.cpp 中加入 constant.h debug.h 以访问变量。

编译时,显示错误**multiple definition** of **IF_DEBUG_ENABLED**

请告诉我实际上我做错了什么。此外,请注意,这是我第一个c / c ++ 应用程序的第一天。我从来没有在学校读过它。

我的代码来源如下: 如

/ - constant.h - /

#ifndef CONSTANT_H
#define CONSTANT_H

const char* APP_NAME            = "ymcmcb";
const bool  IF_DEBUG_ENABLED    = true;

#endif // CONSTANT_H

/ - debug.h - /

#ifndef DEBUG_H
#define DEBUG_H

#include <QString>

class Debug
{  
public:
    static void Log(QString Message);
};

#endif // DEBUG_H

/ - debug.cpp - /

#include "constant.h"
#include "debug.h"

#include "QDebug"

static void Log(QString Message)
{
    if (IF_DEBUG_ENABLED)
        qDebug() << Message;    //It says problem is here
}

/ - main.cpp - /

#include "constant.h"
#include "debug.h"

int main(int argc, char *argv[])
{
    Debug::Log("New application has been run");
}

2 个答案:

答案 0 :(得分:4)

您应该将定义放在.cpp文件中,而不是放在标题中。在标题中,您应该只放置声明extern const bool IF_DEBUG_ENABLED;这将告诉任何代码#include它存在一个名为IF_DEBUG_ENABLED的全局变量。在debug.cpp里面你应该把实际的定义。

警卫只能帮助您防止在单个编译单元中多次定义。但是你有两个编译单元:debug.cpp(加标题)和main.cpp(加标题)。这意味着您在标题中有多个变量定义。


您还有另一个问题:实现静态函数Log()但是应该实现静态类方法Debug::Log()

答案 1 :(得分:2)

C和C ++具有“编译单元”的概念,它本质上是“你告诉我编译的文件中的所有代码加上它包含的所有文件”。

C编译的原始管道是首先运行“预处理器”来读入所有代码,处理宏并定义等,并将得到的代码输出到单个文件中(从内存中,a .i中间文件)

Foo.cpp中

#include "foo1.h"
FOO {
    #include "foo2.h"
}

foo1.h

extern "C" int puts(const char*);
#define FOO int main()

foo2.h

puts("Hello, world\n");

使用g++ -Wall -E -o foo.i foo.cppg++ -Wall -o foo.exe foo.i

进行汇编

foo.i文件如下所示:

# 1 "foo.cpp"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 30 "/usr/include/stdc-predef.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/predefs.h" 1 3 4
# 31 "/usr/include/stdc-predef.h" 2 3 4
# 1 "<command-line>" 2
# 1 "foo.cpp"
# 1 "foo1.h" 1
extern "C" int puts(const char*);
# 2 "foo.cpp" 2

int main() {
# 1 "foo2.h" 1
 puts("Hello, world!\n");
# 5 "foo.cpp" 2
}

这是一个编译单元。这些过程简化了,预处理器内置于编译器本身,但编译单元的概念仍然存在。

您的代码的问题在于您正在定义 - 而不仅仅是在头文件中声明 - IF_DEBUG_ENABLED,因此可能在多个编译单元中。当链接器尝试将已编译的单元组合成可执行文件时,它会查找具有相同名称的变量的多个实例。链接器无法断言它们应该是同一个东西。

要创建在多个编译单元(源文件)之间可见的全局变量或函数,您需要标头声明/原型和源文件定义/实例。

extern bool IF_DEBUG_ENABLED; // variable declaration.
extern void herp();           // function prototype, but
void herp();                  // the extern is optional for functions.

为了能够使用其中任何一个,您现在需要使用实现来备份它们。

源文件

bool IF_DEBUG_ENABLED = true;

这是假设您希望它是运行时变量。另一个选择是使用#define,就像你使用的后卫一样:

constant.h

#ifndef CONSTANT_H // poor choice, there may be a CONSTANT_H somewhere else.
#define CONSTANT_H 1

...
#define IF_DEBUG_ENABLED // comment out to disable

#endif

源:

#if defined(IF_DEBUG_ENABLED)
   qDebug() << message;
#endif

此选项不允许您在运行时更改IF_DEBUG_ENABLED,如果在编译时定义了IF_DEBUG_ENABLED,则“qDebug()&lt;&lt; message”代码仅写入可执行文件。

最后,不是使用#if ... #define ... #endif guard方法,而是可以在文件开头用一行替换所有三个:

constant.h:

#pragma once  //<<-- compiler implements a guard for you.

#include <QString>

class Debug
{  
public:
    static void Log(QString Message);
};