3

我以前多次使用过 include 守卫,但从未真正理解它们是如何工作的或为什么工作的。

为什么以下不起作用?

#ifndef CAMERA_CLASS_HPP
#define CAMERA_CLASS_HPP


class camera_class
{
....
};

camera_class glcam = camera_class();


#endif // CAMERA_CLASS_HPP

错误是这样的:(您可能可以从这个问题的标题中猜到它会是什么!)

-------------- Build: Debug in System ---------------

Linking console executable: bin/Debug/System
/usr/bin/ld: error: obj/Debug/main.o: multiple definition of 'glcam'
/usr/bin/ld: obj/Debug/camera_class.o: previous definition here
/usr/bin/ld: error: obj/Debug/main.glfunc.o: multiple definition of 'glcam'
/usr/bin/ld: obj/Debug/camera_class.o: previous definition here
collect2: ld returned 1 exit status
Process terminated with status 1 (0 minutes, 0 seconds)
0 errors, 0 warnings

另外,有人可以向我解释一下为什么头卫有效吗?

4

6 回答 6

8

标头保护将防止在单个翻译单元中包含多个内容。标头可以(并且正在)包含在多个翻译单元中:

// a.cpp:
#include "camera.hpp"

// b.cpp
#include "camera.hpp"

这将产生 aa.obj和 a b.obj,每个都包含 的定义glcam。当链接在一起以产生最终的二进制文件时,您会得到多重定义错误。

您需要在标头中声明并在文件中只 定义一次:glcam.cpp

// camera.hpp
...

extern camera_class glcam;

// camera.cpp
#include "camera.hpp"

camera_class glcam;
于 2012-09-07T14:48:20.333 回答
4

根本原因:标头保护防止在同一个翻译单元
中多次包含相同的标头,但不能跨不同的翻译单元。当您在多个翻译单元中包含相同的头文件时,确实会在包含该头的每个翻译单元中创建 一个副本。 C++ 标准要求每个符号只能定义一次(一个定义规则),因此链接器会向您发出错误。
glcam

解决方法:
不要glcam在头文件中创建。相反,它应该以只定义一次的方式创建。正确的方法是使用关键字extern

于 2012-09-07T14:47:37.503 回答
3

看起来您想创建一个glcam可以在多个地方使用的单个对象。我会通过公开一个免费函数来返回一个static实例来做到这一点。这类似于 using extern,但我发现它的意图更加明确。

#ifndef CAMERA_CLASS_HPP
#define CAMERA_CLASS_HPP


class camera_class
{
....
};

camera_class& get_camera();


#endif // CAMERA_CLASS_HPP

// in the CPP

camera_class& get_camera()
{
   static camera_class the_camera;
   return the_camera;
}

camera_class这使您能够在不依赖的情况下使用单个实例extern,但同时不会强迫您将其用作单例,因为代码的其他区域也可以自由创建自己的私有实例。

这可以按原样(自由函数)或static作为camera_class. 根据 Scott Meyers 的一些极好的建议,我选择了前者:

如果您正在编写一个可以实现为成员或非朋友非成员的函数,您应该更喜欢将其实现为非成员函数。

资料来源:http ://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/184401197

于 2012-09-07T14:52:15.210 回答
2

由于您从多个文件中包含此文件,因此您违反了一个定义规则

在整个程序中,一个对象或非内联函数不能有多个定义

您应该将glcam定义放在源文件中,而不是放在头文件中,或者将其声明为extern,并在某个源文件中提供定义。

于 2012-09-07T14:48:56.693 回答
1

包含保护可防止标题中文本的多个实例出现在一个编译单元中(即,您正在构建的单个 .cpp 被构建到 .o 中)

它不会阻止该文本的多个实例出现在多个编译单元中。

所以在链接时,每个包含这个头文件的编译单元都有一个

 camera_class glcam = camera_class();

作为一个象征。当提到“glcam”时,C++ 无法决定您指的是哪个单一全局定义。来自 main.o 的那个还是来自 camera_class.o 的那个?

于 2012-09-07T14:48:42.917 回答
0

它工作得很好,你在每个源文件中只得到一个定义。

问题是您有多个源文件并且链接器正在查找多个定义。

在头文件中,您应该输入:

extern camera_class glcam;

然后在一个且只有一个源文件中将您以前在标题中包含的内容:

camera_class glcam = camera_class();

此时您需要注意初始化顺序问题。不要尝试glcam从任何静态对象中使用。

于 2012-09-07T14:49:41.780 回答