【问题标题】:Error with multiple definitions of function多个函数定义错误
【发布时间】:2013-07-28 02:21:27
【问题描述】:

几年前参加了入门课程后,我正在尝试重新学习 C++,但遇到了一些基本问题。我当前的问题是在尝试使用朋友功能时出现的。这是我在 2 个文件中的代码。

第一:

// fun.cpp

#include <iostream>
using namespace std;

class classA {
    friend void funct();
public:
    classA(int a=1,int b=2):propa(a),propb(b){cout<<"constructor\n";}
private:
    int propa;
    int propb;
    void outfun(){
        cout<<"propa="<<propa<<endl<<"propb="<<propb<<endl;
    }
};
void funct(){                     // ERROR HERE
    cout<<"enter funct"<<endl;
    classA tmp(1,2);
    tmp.outfun();
    cout<<"exit funct"<<endl;
}

第二:

// mainfile.cpp
#include <iostream>
#include "fun.cpp"
using namespace std;

int main(int nargin,char* varargin[]) {
    cout<<"call funct"<<endl;
    funct();
    cout<<"exit main"<<endl;
    return 0;
}

我得到的错误是“`funct()' 的多重定义”。将其声明为友元函数时是否使用了错误的语法?

【问题讨论】:

    标签: c++ definition friend


    【解决方案1】:

    这是一个高度简化但希望相关的视图,说明使用 C++ 构建代码时会发生什么。

    C++ 将生成机器可执行代码的负载分为以下不同阶段 -

    1. 预处理 - 这是您可能正在使用的任何宏 - #defines 等扩展的地方。

    2. 编译 - 每个 cpp 文件以及该文件中的所有 #included 文件直接或间接(统称为编译单元)被转换为机器可读的目标代码。

      这也是 C++ 检查所有已定义函数的地方(即在 { } 中包含一个主体,例如 void Foo( int x){ return Boo(x); }) 以有效的方式引用其他函数。

      这样做的方式是坚持在调用之前至少提供这些其他函数的声明(例如 void Boo(int); ),以便它可以检查您是否正确调用它。这可以直接在调用它的 cpp 文件中完成,或者通常在包含的头文件中完成。

      请注意,只有与此 cpp 中定义的函数和包含文件相对应的机器代码才会被构建为此编译单元的对象(二进制)版本(例如 Foo),而不是仅仅声明的那些(例如 Boo)。

    3. 链接 - 这是 C++ 寻找在每个编译单元中声明和调用的内容并将其链接到被调用的位置的阶段。现在,如果没有找到这个函数的定义,链接器就会放弃并出错。同样,如果它发现同一个函数签名的多个定义(本质上是它所采用的名称和参数类型),它也会出错,因为它认为它不明确并且不想任意选择一个。

    后者是您的情况。通过对fun.cpp 文件执行#includefun.cppmainfile.cpp 都有funct() 的定义,并且链接器不知道在您的程序中使用哪个并抱怨它。

    Vaughn 上面提到的解决方法是不将具有 funct() 定义的 cpp 文件包含在 mainfile.cpp 中,而是将 funct() 的声明移动到单独的头文件中,并将其包含在 mainline.cpp 中。这样,编译器将获得 funct() 的声明以使用,而链接器将仅从 fun.cpp 获得 funct() 的一个定义,并将放心使用它。

    【讨论】:

      【解决方案2】:

      问题是,如果你在程序的两个地方包含 fun.cpp,你最终会定义它两次,这是无效的。

      您不想包含cpp 文件。你想包含头文件。

      头文件应该只有类定义。您将单独编译的相应cpp 文件将具有函数定义。

      fun.hpp:

      #include <iostream>
      
      class classA {
          friend void funct();
      public:
          classA(int a=1,int b=2):propa(a),propb(b){std::cout<<"constructor\n";}
      private:
          int propa;
          int propb;
          void outfun(){
              std::cout<<"propa="<<propa<<endl<<"propb="<<propb<< std::endl;
          }
      };
      

      fun.cpp:

      #include "fun.hpp"
      
      using namespace std;
      
      void funct(){
          cout<<"enter funct"<<endl;
          classA tmp(1,2);
          tmp.outfun();
          cout<<"exit funct"<<endl;
      }
      

      mainfile.cpp:

      #include <iostream>
      #include "fun.hpp"
      using namespace std;
      
      int main(int nargin,char* varargin[]) {
          cout<<"call funct"<<endl;
          funct();
          cout<<"exit main"<<endl;
          return 0;
      }
      

      注意,一般建议在头文件中避免using namespace std

      【讨论】:

      • 它还可以帮助一些链接器包装标头保护——在 #ifndef 上搜索
      • @CarlNorum 我的印象是多个定义错误是链接器错误而不是编译。但也许我错了。
      • 他们是,但头后卫与此无关。好吧,除非你在标题中做一些疯狂的事情,否则就是这样。
      • 为什么它适用于类而不适用于函数?与需要将其放置在 cpp 文件中的类定义相比,我看不出函数有什么特别之处。
      • @ElefEnt:一个类纯粹是一个编译时实体。如果您定义了一个从未使用过的类,那么编译器将不会创建任何机器代码。然而,非内联函数定义意味着实际创建机器代码,无论您是否使用它。您只希望将该机器代码存储在一个目标文件中。如果将函数定义放在标头中,则意味着在包含该标头的任何位置都创建机器代码。
      【解决方案3】:

      出现此问题是因为您调用的是fun.cpp 而不是fun.hpp。所以c++编译器发现func.cpp定义两次,抛出这个错误。

      main.cpp 文件的第 3 行从 #include "fun.cpp" 更改为 #include "fun.hpp"

      【讨论】:

        【解决方案4】:

        您在mainfile.cpp 中有#include "fun.cpp",所以编译时使用:

        g++ -o hw1 mainfile.cpp
        

        会起作用,但是如果你通过像这样将它们链接在一起来编译

        g++ -g -std=c++11 -Wall -pedantic   -c -o fun.o fun.cpp
        g++ -g -std=c++11 -Wall -pedantic   -c -o mainfile.o mainfile.cpp
        

        正如他们上面提到的,添加#include "fun.hpp" 将需要完成,否则它将不起作用。但是,您使用 funct() 函数的情况与我的问题略有不同。

        我在进行硬件分配和通过较低 bash 配方编译的自动分级器时遇到了这个问题,但在本地它使用较高的 bash 工作。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2022-09-29
          • 2015-04-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多