我之前曾多次使用过包含警卫,但从未真正理解他们的工作方式或原因。
为什么以下不起作用?
#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
另外,有人可以向我解释为什么头部防守工作吗?
答案 0 :(得分:8)
标题保护将防止单个翻译单元中的多个包含。标题可以(并且正在)包含在多个翻译单元中:
// a.cpp:
#include "camera.hpp"
// b.cpp
#include "camera.hpp"
这将产生a.obj
和b.obj
,每个glcam
包含glcam
的定义。当链接在一起产生最终二进制时,您会得到多重定义错误。
您需要在标头中声明 .cpp
,并在// camera.hpp
...
extern camera_class glcam;
// camera.cpp
#include "camera.hpp"
camera_class glcam;
文件中定义一次:
{{1}}
答案 1 :(得分:4)
根本原因:
标题保护可以防止在同一 translation unit 中多次包含相同的标题,但不能跨越不同的翻译单元。当您在多个翻译单元中包含相同的头文件时,然后,
确实会在包含标题的每个翻译单元中创建glcam
的副本
C ++标准要求每个符号只能 defined 一次( One Definition Rule )因此链接器会向您发出错误。
<强>解决方案:
不要在头文件中创建glcam
。相反,它应该以这样的方式创建,即它只被定义一次。正确的方法是 using the keyword extern
。
答案 2 :(得分:3)
看起来您想创建一个可以在多个地方使用的glcam
个对象。我会通过公开一个自由函数来返回一个static
实例。这与使用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的一些出色建议,我选择了前者:
如果您正在编写一个可以实现为a的函数 成员或作为非朋友的非成员,您应该更愿意实施 它作为非会员职能。
来源:http://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/184401197
答案 3 :(得分:2)
由于您要从多个文件中包含此文件,因此您违反了One Definition Rule:
在整个程序中,对象或非内联函数不能有 不止一个定义
您应该将glcam
定义放在源文件中,而不是放在头文件中,或者将其声明为extern
,并在某个源文件中提供定义。
答案 4 :(得分:1)
include guard会阻止标题中的多个文本实例出现在一个编译单元中(即您构建的单个.cpp内置到.o中)
它不会阻止该文本的多个实例出现在多个编译单元中。
因此,在链接时,包含此标头的每个编译单元都有一个
camera_class glcam = camera_class();
作为一种象征。 C ++无法决定何时引用“glcam”这个单一的全局定义。来自main.o的那个或来自camera_class.o的那个?
答案 5 :(得分:0)
它工作得很好,你只在每个源文件中得到一个定义。
问题是您有多个源文件,链接器正在查找多个定义。
在头文件中,您应该输入:
extern camera_class glcam;
然后在一个且只有一个源文件中放置您曾经拥有的标题:
camera_class glcam = camera_class();
此时您需要了解初始化顺序问题。不要试图从任何静态对象中使用glcam
。