【问题标题】:C++ class redefinition error - Help me understand headers and linkingC++ 类重新定义错误 - 帮助我理解标题和链接
【发布时间】:2013-01-07 06:10:04
【问题描述】:

我开始用 C++ 编写一个简单的解释器,它具有我将在下面描述的类结构,但我退出并用 Java 重写了这个东西,因为头文件给我带来了困难。这是 C++ 中显然不允许的基本结构:

ma​​in.cpp 包含 main 函数,并包含一个我们可以调用 printer.h 的类的头文件(其单个 void 方法在 printer.h 中实现) cpp)。现在想象另外两个相同的类。两者都想打电话给Printer::write_something();,所以我在每个中都包含了printer.h。所以这是我的第一个问题:为什么我可以#include <iostream> 一百万次,甚至一个接一个,但我只能包含一次 my 标题? (好吧,我想我可以用我的做同样的事情,只要它在同一个文件中。但我可能是错的。)我理解声明和实现/定义之间的区别,但该代码给了我一个类重定义错误。我不明白为什么。这就是让我大吃一惊的事情(可能会告诉你为什么我什么都不明白):我不能只在 ma​​in.cpp 的顶部包含 printer.h 并使用我其他两个班级的班级。我知道我可以毫无问题地将 printer.h 包含在两个类(标题)中的 一个 中,但我不明白为什么这与仅包含它有什么不同在我将类包含在 ma​​in.cpp 之前(因为这样做会给我一个找不到类的错误)。

当我厌倦时,我考虑转向 C,因为我使用的 OOP 无论如何都非常强制,但除非我将所有内容都写在一个文件中,否则我会遇到同样的问题。知道 C++ 却因为编译问题而无法正确使用它令人沮丧。

如果您能帮我解决这个问题,我将不胜感激。谢谢!

【问题讨论】:

  • 您是否熟悉包含/标题保护(请参阅en.wikipedia.org/wiki/Include_guard)。 iostream 库应该有它们,而您的 printer.h 标头很可能没有。标头保护试图确保只使用一个标头定义。
  • 我最初尝试过,但没有成功。也许是因为我只在两个标题之一中使用了它们。
  • 另外,作为重新阅读您的问题后的注释。这不是 C++ 独有的,如果printer.h 声明了一个函数并且printer.c 定义了它的实现(你仍然需要标头保护),你会在C 中遇到同样的问题。对于 C 中的类似问题,请参阅:stackoverflow.com/questions/5430019/…

标签: c++ header linker redefinition


【解决方案1】:

C/C++ 编译分为编译/翻译单元以生成目标文件。 (.o, .obj)

see here the definition of translation unit

C/C++ 文件中的#include 指令直接等效于同一文件中的简单递归复制粘贴。您可以尝试将其作为实验。

因此,如果同一个翻译单元包含两次相同的标头,编译器会看到某些实体被多次定义,如果您将它们写入同一个文件中就会发生这种情况。错误输出将完全相同。

语言中没有内置的保护机制可以阻止您执行多个包含,因此您必须为每个 C/C++ 标头编写包含保护或特定的#pragma 样板。

【讨论】:

    【解决方案2】:

    为什么我可以#include 一百万次,甚至一个接一个,但我只能包含一次标题?

    这可能是因为您的标题没有include guard

    // printer.h file
    #ifndef PRINTER_H_
    #define PRINTER_H_
    
     // printer.h code goes here
    
    #endif
    

    请注意,最好为包含保护定义选择更长的名称,以最大程度地减少两个不同标头可能具有相同标头的可能性。

    【讨论】:

    • 我还需要包含我的正常标题吗?当我第一次遇到这个问题时,我使用它作为解决方案,但它不起作用,可能是因为我认为命名会自动连接到实际的标题。 (我以为PRINTER_H和printer.h一样)
    • @AVR 那应该是你的标题。换句话说,修改所有标题,使它们都包含保护,当然名称是唯一的。
    • 啊哈,应该注意你的 // 评论。还有一个问题:这是否告诉编译器/链接器不要重新定义类?我想我的问题是编译器不知道如何将标头再次链接到实现。
    【解决方案3】:

    大多数头文件应该包含在包含保护中:

    #ifndef MY_UNIQUE_INCLUDE_NAME_H
    #define MY_UNIQUE_INCLUDE_NAME_H
    
    // All content here.
    
    #endif
    

    这样,编译器在每个翻译单元中只会看到一次标头的内容。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-04-07
    • 1970-01-01
    • 1970-01-01
    • 2011-02-26
    • 1970-01-01
    • 1970-01-01
    • 2011-10-27
    • 1970-01-01
    相关资源
    最近更新 更多