通过在.cpp而不是标头中定义来解决LNK2005错误。为什么?

时间:2015-01-17 21:11:41

标签: c++

所以我有一个旨在从文件中读取的函数。它在FileIO.h中声明和定义,如下所示:

std::string ReadFromFile(std::ifstream& in)
{
    std::string out;
    in >> out;
    return out;
}

现在,我将此标题包含在另一个标题中,该标题本身包含在另一个标题中,该标题包含在main中。即使它没有被包含在多个内容中,但我在所有头文件上都有#ifndef-#def-#endif预处理器,并且具有唯一的名称。

我无法为我的生活弄清楚为什么它仍然抛出链接器错误(事实上,它抛出了两次:一次在我的Main中,一次在第二个头文件中)。然后我尝试在FileIO.h中声明函数,如下所示:

std::string ReadFromFile(std::ifstream& in);

然后我在FileIO.cpp中定义了它。没有更多链接器错误!这是因为我在函数中定义了变量,并且它们正在编译,然后在后面看到(例如std::string out),还是来自其他东西?我很喜欢这个有效的原因。谢谢!

2 个答案:

答案 0 :(得分:5)

你可能对你的包含警卫看起来不起作用感到困惑。这是这个难题的缺失部分:

编译器根据各个翻译单元处理您的代码。这只是“一个.cpp文件和它直接或间接包含的标题集”的一个奇特术语 - 重要的是每个TU都是独立于其他所有TU编译的。

当编译器处理时,例如main.cpp它最终会看到您的标题,并在生成的ReadFromFile中包含已编译的main.obj。如果这是您项目中唯一的TU,那么在您实施ReadFromFile的具体位置上没有任何区别。

但是如果你有另一个TU,例如stuff.cpp也最终会引入定义ReadFromFile的标题然后你就会遇到问题:该函数最终出现在main.objstuff.obj中,这会让链接器抱怨,因为现在你最终违反了One Definition Rule

请注意,在每个单独的TU中,您的包含警卫按预期工作:无论您尝试拉入相同的标头多少次,编译器在处理每个包时都不会抱怨。

答案 1 :(得分:0)

您遇到链接器错误,因为在多个目标文件中实现了相同的功能,因为它是在头文件中实现的。

你可以通过在函数前使用static inline来避免它。