C++包括警卫似乎不起作用?

C++ include guard doesn't appear to work?

本文关键字:不起作用 包括警 C++      更新时间:2023-10-16

我以前多次使用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

另外,有人能向我解释一下为什么头球后卫能起作用吗?

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

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

这将产生a.objb.obj,每个都包含glcam的定义。当链接在一起生成最终的二进制文件时,会出现多重定义错误。

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

// camera.hpp
...
extern camera_class glcam;
// camera.cpp
#include "camera.hpp"
camera_class glcam;

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

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

看起来您想要创建一个可以在多个地方使用的单个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;
}

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

这可以按原样(自由函数)或作为camera_classstatic成员函数来实现。根据Scott Meyers的一些优秀建议,我选择了前者:

如果您正在编写一个函数,该函数可以实现为成员或作为非朋友非成员,您应该更愿意实现它是一个非成员函数。

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

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

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

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

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

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

因此,在链接时,包含此标头的每个编译单元都有一个

 camera_class glcam = camera_class();

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

它运行得很好,在每个源文件中只得到一个定义。

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

在头文件中,您应该放:

extern camera_class glcam;

然后在一个也是唯一一个源文件中,将您过去拥有的内容放在标题中:

camera_class glcam = camera_class();

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