我在标题
中定义了一个字符数组//header.h
const char* temp[] = {"JeffSter"};
标头如果#defined受到保护并且在顶部有一次#pragma。如果这个头包含在多个地方,我得到一个LNK4006 - 已经在blahblah.obj中定义的char const * * temp。所以,我对此有几个问题
答案 0 :(得分:14)
如果我有防护装置,为什么会发生这种情况?我认为他们阻止了首次访问后读取标题。
包含警卫确保在一个文件(翻译单位)中仅包含一次标题。对于包含标题的多个文件,您希望标题包含在每个文件中。
通过定义,而不是在头文件中使用外部链接(全局变量)声明变量,您只能在一次源文件中包含标头。如果在多个源文件中包含标题,则会有多个变量定义,这在C ++中是不允许的。
因此,正如您所知,为了上述原因,在头文件中定义变量是一个坏主意。
为什么此标题中的众多枚举还没有给出LNK4006警告?
因为它们没有定义“全局变量”,它们只是关于类型等的声明。它们不保留任何存储空间。
如果我在签名前添加静态,我不会收到警告。这样做有什么意义。
制作变量static
时,它具有静态范围。该对象在定义它的转换单元(文件)之外是不可见的。所以,简单来说,如果你有:
static int i;
在标题中,您在其中包含标题的每个源文件都会获得一个单独的 int
变量i
,该变量在源文件之外是不可见的。这称为内部链接。
有没有更好的方法可以避免错误,但是让我在标题中声明数组。我真的很讨厌只有一个cpp文件用于数组定义。
如果您希望数组是所有C ++文件中可见的一个对象,则应该执行以下操作:
extern int array[SIZE];
在头文件中,然后在所有需要变量array
的C ++源文件中包含头文件。在源(.cpp
)文件的一个中,您需要定义array
:
int array[SIZE];
您也应该在上面的源文件中包含标题,以便捕获由于标题和源文件不同而导致的错误。
基本上,extern
告诉编译器“array
已在某处定义,并且类型为int
,大小为SIZE
”。然后,您实际上只定义 array
一次。在链接阶段,一切都很好地解决了。
答案 1 :(得分:4)
包含防护装置可以防止您重复将相同的标题包含在同一个文件中 - 但不能将其包含在不同的文件中。
发生的事情是链接器在多个目标文件中看到temp
- 你可以通过使temp
静态或将其放入未命名的命名空间来解决这个问题:
static const char* temp1[] = {"JeffSter"};
// or
namespace {
const char* temp2[] = {"JeffSter"};
}
或者,您可以使用一个定义temp
的源文件,并在标题中将其声明为extern:
// temp.cpp:
const char* temp[] = {"JeffSter"};
// header.h:
extern const char* temp[];
答案 2 :(得分:4)
标题保护与阻止整个程序中的多个定义完全无关。标头保护的目的是防止多个相同的头文件包含在相同的翻译单元(。cpp文件)中。换句话说,它们的存在是为了防止同一源文件中的多个定义。并且他们确实按照您的意图工作。
管理C ++中多定义问题的规则称为“一个定义规则”(ODR)。对于不同类型的实体,ODR的定义不同。例如,允许 types 在程序中具有多个相同的定义。他们可以(并且大多数具有)在每个使用它们的翻译单元中定义。这就是您的枚举定义不会导致错误的原因。
具有外部链接的对象是一个完全不同的故事。它们必须在一个且仅一个翻译单元中定义。这就是为什么当您将头文件包含到多个翻译单元中时,temp
的定义会导致错误。包含警卫无法阻止此错误。只是不要在头文件中定义具有外部链接的对象。
通过添加static
,您可以为对象提供内部链接。这将使错误消失,因为从ODR的角度来看现在完全可以。但是这将在每个翻译单元中定义一个独立的temp
对象,其中包含头文件。为了达到同样的效果,你也可以做到
const char* const temp[] = { "JeffSter" };
因为C ++中的const
对象默认具有内部链接。
这取决于您是否需要具有外部链接的对象(即整个程序的一个对象)或具有内部链接的对象(每个翻译单元都是唯一的)。如果您需要后者,请使用static
和/或额外const
(如果适用于您),如上所示。
如果您需要前者(外部链接),则应将非定义声明放入头文件
extern const char* temp[];
并将定义移动到一个且只有一个.cpp文件
char* const temp[] = { "JeffSter" };
头文件中的上述声明适用于大多数用途。但是,它将temp
声明为未知大小的数组 - 不完整的类型。如果您希望将其声明为已知大小的数组,则必须手动指定大小
extern const char* temp[1];
并记住在声明和定义之间保持同步。
答案 3 :(得分:0)
我恭敬地反对在标题中定义变量的建议,因为我认为“从不”过于宽泛。尽管如此,让我进入这个主题的那一集为那些敢于这样做的人提供了警示。
作为对LNK4006警告原因的调查结果,我登陆了这个页面,调用了一个长期建立的数组,我刚从定义我的DLLMain例程的转换单元移动到大多数包含的私有头中。构成此库的翻译单元。在过去的11年里,我已经数百次编译了这个库,我以前从未见过这个警告。
在我读完这个页面后不久,我发现了错误的原因,即定义在保护块之外,保护模块中定义的其他所有其他定义DLLMain的内容,这是我通常收集所有内容的地方需要外部链接的内存块。正如预期的那样,移动防护区内的桌子消除了警告,只留下了两个与全新的外部链接表有关的问题。
外卖:你可以在标题中定义变量,它是放置常见块的好地方,但请注意你的警卫。
答案 4 :(得分:-1)
坚持......你正在混淆你的声明......你确实在你的头文件中说'char const * * temp'你有'const char * temp [] = {“JeffSter”};'。
参见{第3节}第6节“数组和指针”中的第6.1节,引用:
6.1: I had the definition char a[6] in one source file, and in another I declared extern char *a. Why didn't it work? A: In one source file you defined an array of characters and in the other you declared a pointer to characters. The declaration extern char *a simply does not match the actual definition. The type pointer-to-type-T is not the same as array-of-type-T. Use extern char a[]. References: ISO Sec. 6.5.4.2; CT&P Sec. 3.3 pp. 33-4, Sec. 4.5 pp. 64-5.
这是问题的根源。匹配您的声明和定义。对不起,如果这听起来很生硬,但我无法注意到链接器告诉你的内容......
希望这有帮助, 最好的祝福, 汤姆。