多个函数定义出错

Ada*_*yos 44 c++ definition friend

我在几年前参加了一个入门课程后试图重新学习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;
}
Run Code Online (Sandbox Code Playgroud)

第二:

// 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;
}
Run Code Online (Sandbox Code Playgroud)

我得到的错误是"funct()'的多重定义".在将其声明为友元函数时,我使用了错误的语法吗?

Moh*_*ugh 60

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

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

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

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

    这是C++还检查所有定义的函数(即包含{ }例如 正文以void Foo( int x){ return Boo(x); })有效方式引用其他函数)的地方.

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

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

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

后者就是你的情况.通过做#include了的fun.cpp文件,都fun.cppmainfile.cpp有一个定义funct()和链接器不知道在你的程序中使用哪一个和抱怨它.

上面提到的Vaughn的修复是不包含带有funct()in 的定义的cpp文件,mainfile.cpp而是funct()在单独的头文件中移动声明并将其包括在中mainline.cpp.通过这种方式,编译器将获得使用的声明,funct()并且链接器将只获得funct()from的一个定义,fun.cpp并将自信地使用它.

  • @MartinSmith 我得出了同样的结论,直到一位同事纠正了我:不,相反,将 `fun.cpp` 包含到 `mainfile.cpp` 中创建了*两个* `funct()` 定义(一个在 `fun .cpp` 和 `mainfile.cpp` 中的一个)。包含 `fun.cpp` 会将其内容复制并粘贴到 `mainfile.cpp` 中,包括 `funct()` 的定义。这就是为什么包含实现文件而不是头文件是不好的做法。:p (3认同)

Vau*_*ato 26

问题是如果你在程序的两个地方包含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;
    }
};
Run Code Online (Sandbox Code Playgroud)

fun.cpp:

#include "fun.hpp"

using namespace std;

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

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;
}
Run Code Online (Sandbox Code Playgroud)

请注意,通常建议避免using namespace std在头文件中使用.

  • 它也可以帮助一些链接器包装标题保护 - 搜索#ifndef (3认同)
  • 他们是,但是头卫并没有与此有任何关系.好吧,除非你在标题中做了一些疯狂的事情,那就是. (3认同)
  • 为什么它适用于类而不是函数?与要求将其放在cpp文件中的类定义相比,我没有看到函数的特殊之处. (2认同)
  • @ElefEnt:一个类纯粹是一个编译时实体。如果您定义了一个从未使用过的类,那么编译器将不会创建任何机器代码。然而,非内联函数定义意味着实际创建机器代码,无论您是否使用它。您只想将该机器代码存储在一个目标文件中。如果您将函数定义放在标题中,则意味着在包含该标题的任何地方都创建机器代码。 (2认同)