60

几年前参加了入门课程后,我正在尝试重新学习 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()'的多重定义”。将其声明为友元函数时是否使用了错误的语法?

4

4 回答 4

84

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

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

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

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

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

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

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

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

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

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

于 2013-07-28T06:26:09.117 回答
37

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

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

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

有趣的.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;
    }
};

有趣的.cpp:

#include "fun.hpp"

using namespace std;

void funct(){
    cout<<"enter funct"<<endl;
    classA tmp(1,2);
    tmp.outfun();
    cout<<"exit funct"<<endl;
}

主文件.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在头文件中。

于 2013-07-28T03:24:50.017 回答
1

你有#include "fun.cpp"这样的mainfile.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 工作。

于 2021-01-18T06:56:10.457 回答
1

发生此问题是因为您正在调用fun.cpp而不是fun.hpp. 因此 c++ 编译器两次找到 func.cpp 定义并抛出此错误。

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

于 2020-07-21T17:16:16.863 回答