【问题标题】:Multiple definitions error in C++C ++中的多个定义错误
【发布时间】:2016-06-01 20:43:40
【问题描述】:

我正在编写一个 C++ 程序,其中每个文件都有自己的一组全局变量声明。这些文件中的大多数使用在其他文件中使用 extern 定义的全局变量。

这是一个类似于我的程序的示例:

Main.cpp

#include "stdafx.h"
#include <iostream>
#include "Other_File.cpp"

int var1;
int var2;

int main()
{
    var1 = 1;
    var2 = 2;
    otherFunction();
    var4 = 4; // From Other_File.cpp

    std::cout << var1 << " " << var2 << " " << var3 << " " << var4 << std::endl;

    return(0);
}

Other_File.cpp

extern int var1;
extern int var2;

int var3;
int var4;

void otherFunction()
{
     var3 = var1 + var2;
     var4 = 0;
}

当我在 Visual Studio (Windows) 中构建此代码时,一切运行正常且输出正确。但是当我尝试在 Linux 上使用 g++ 构建时,我收到以下错误:

g++ -o 测试Testing.o Other_File.o Other_File.o:(.bss+0x0): var3' Testing.o:(.bss+0x0): first defined here Other_File.o:(.bss+0x4): multiple definition ofvar4' 的多重定义 Testing.o:(.bss+0x4): 这里首先定义 Other_File.o: In function otherFunction()': Other_File.cpp:(.text+0x0): multiple definition of otherFunction()' Testing.o:Testing.cpp:(.text+0x0): 首先定义 这里 collect2: ld 返回 1 退出状态 make: *** [Testing] Error 1

这是因为我在主文件中“包含”了另一个文件吗?

如果不是,我的代码有什么问题?

编辑:这是我的 Makefile for g++ 的内容:

Testing: Testing.o Other_File.o
    g++ -o Testing Testing.o Other_File.o

Testing.o: Testing.cpp
    g++ -c -std=c++0x Testing.cpp

Other_File.o: Other_File.cpp
    g++ -c -std=c++0x Other_File.cpp

clean:
    rm *.o Calculator

【问题讨论】:

  • 按照惯例,不应在另一个 cpp 文件中包含 cpp 文件。
  • 你传递给 g++ 的命令行是什么?
  • 是的,通过包含 cpp 文件,您最终有两次定义 - 在原始 cpp 文件和包含它的文件中(我假设您也在独立编译包含的 cpp 文件)。 您不得包含 CPP 文件
  • 我怀疑您在 Visual Studio 中的项目中只有 Main.cpp,但是在 Linux 上构建时您会编译这两个 cpp 文件。因此,请检查您在 Linux 上编译的确切文件。另请参阅上面 Brian 的评论。

标签: c++ compilation linkage


【解决方案1】:

不要将一个源文件#include 转换成另一个源文件。在某些时间和地点可以这样做,但只有不到 0.001% 的程序需要这样做。

您应该做的是创建一个 header 文件,其中包含两个源文件中所需内容的声明

那么您的代码将如下所示:

  1. main.cpp源文件

    #include "stdafx.h"
    #include <iostream>
    #include "Other_File.h"  // Note inclusion of header file here
    
    int var1;
    int var2;
    
    int main()
    {
        var1 = 1;
        var2 = 2;
        otherFunction();
        var4 = 4; // From Other_File.cpp
    
        std::cout << var1 << " " << var2 << " " << var3 << " " << var4 << std::endl;
    }
    
  2. other_file.cpp 源文件,就像你现在拥有的一样

  3. other_file.h头文件,新建文件

    #pragma once
    
    // Declare the variables, so the compiler knows they exist somewhere
    extern int var3;
    extern int var4;
    
    // Forward declaration of the function prototype
    void otherFunction();
    

这两个源文件将被分别编译并链接在一起以形成最终的可执行文件。这个链接步骤是你的构建失败的地方。它会注意到other_source.cpp 中定义的变量是在从该源文件创建的目标文件中定义的,但是由于您将其包含到main.cpp 源文件中,因此从该源文件创建的目标文件也是如此。

这就是为什么你需要了解translation units,这是编译器实际看到的。一个 C++ 源文件经过许多phases of translation,每个都有自己的特殊部分。 翻译单元大概是一个包含所有标题的单一源文件。

这也是了解preprocessor #include directive 功能的好理由。它基本上将包含的文件按原样插入到正在预处理的源文件中。 #include 指令所在的位置,经过预处理后将是包含文件的内容,这就是编译器将看到的内容。

【讨论】:

  • other_file.cpp 不应包含 extern int var1; extern int var2; - 它们也应该放在一个公共头文件中(无论是 other_file.h 还是其他)
  • 出于好奇,什么时候会包含源文件(这里的源文件是指编译成它自己的目标文件的东西)
  • @SergeyA 我在一些低级直接到硬件的简单 RTOS 代码中看到了它,但是包含的源文件没有编译成链接的目标文件。
  • @JoachimPileborg,所以它不是一个真正的翻译单元,只是一个恰好有 .c 扩展名的文件?
  • 有没有办法让我按照现在程序设置的方式在 Linux 上编译它? (无论效率如何)
【解决方案2】:

在编译器开始工作之前,包含有效地将包含的文件粘贴到包含文件中,所以编译器看起来是这样的:

#include "stdafx.h"
#include <iostream>
// #include "Other_File.cpp" sub in other file here
extern int var1;
extern int var2;

int var3;
int var4;

void otherFunction()
{
     var3 = var1 + var2;
     var4 = 0;
}


int var1;
int var2;

int main()
{
    var1 = 1;
    var2 = 2;
    otherFunction();
    var4 = 4; // From Other_File.cpp

    std::cout << var1 << " " << var2 << " " << var3 << " " << var4 << std::endl;

    return(0);
}

这本身不是问题。看起来很有趣,但这不是问题。没有重复的变量。

extern int var1;

告诉编译器在其他地方存在var1。稍后会找到并链接。继续。如果您撒谎,链接器会抱怨。在这种情况下,它大约下降了 10 行。

在编译 Other_File.cpp 并与 Main.cpp 链接时出现问题。链接器在 Main.o 和 Other_File.o 中都找到了 var3var4 的完整定义

您可以从构建中排除 Other_File.cpp,这可能是 Visual Studio 为您所做的,除了严重违反约定外,一切都会很好:永远不要包含 .cpp 文件。

为什么?因为按照惯例,cpp 文件定义了东西并使它们成为现实。您可以通过在标头中定义变量或函数来破坏 .h 文件。例如:

#ifndef BADHEADER_H_
#define BADHEADER_H_


int fubar; // don't do this
/* do this instead:
extern int fubar;
and place int fubar; into the most logical cpp file
*/


#endif /* BADHEADER_H_ */

现在每个包含 badheader.h 的人都有一个名为 fubar 的变量,无论他们是否想要它,当链接器出现来组装程序时,哪个 fubar 是真正的 fubar?他们都是。哎呀。糟糕的链接器所能做的就是吐出一个错误并等待您修复歧义。

在一天结束的时候,你可以#include 任何东西。它不会编译,但您可以包含 Word 文档。你甚至可以表演一些小技巧,比如

int array[] =
{
#include "filtercoefs.h"
};

array.h 是简单的

1,2,3,4,5

当您使用 Matlab 之类的工具输出滤波器系数时很有用,但更喜欢遵守约定以防止与其他编码人员混淆。

不管怎样,C++ 可以让你做各种各样的事情。他们中的大多数你最好在做之前考虑几次。

【讨论】:

    猜你喜欢
    • 2011-07-19
    • 2022-09-29
    • 2016-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多