函数有多个定义的错误

23 浏览
0 Comments

函数有多个定义的错误

我在几年前上过一门入门课程,现在正在尝试重新学习C++,但是遇到了一些基本问题。我目前的问题出现在尝试使用友元函数时。以下是我的代码,分为两个文件。

第一个文件:

// fun.cpp
#include 
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() {                     // 在这里出错
    cout << "enter funct" << endl;
    classA tmp(1,2);
    tmp.outfun();
    cout << "exit funct" << endl;
}

第二个文件:

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

我得到的错误是"multiple definition of `funct()'"。在声明为友元函数时,我使用的语法是否有误?

0
0 Comments

这个问题的出现是因为你在调用fun.cpp而不是fun.hpp。所以C++编译器找到了两次func.cpp的定义,并抛出了这个错误。

为了解决这个问题,你需要修改main.cpp文件的第3行,将#include "fun.cpp"改为#include "fun.hpp"

0
0 Comments

C++代码在编译过程中会经历预处理、编译和链接等阶段。预处理阶段会展开宏定义。编译阶段将每个cpp文件及其直接或间接包含的头文件(称为编译单元)转换成机器可读的目标代码。在编译阶段,C++会检查所有定义了函数体(例如`void Foo(int x){ return Boo(x); }`)的函数是否以有效的方式引用了其他函数。为了进行检查,C++要求在调用函数之前至少提供函数的声明(例如`void Boo(int);`),以便检查调用是否正确。声明可以直接放在调用函数的cpp文件中,或者通常放在包含的头文件中。需要注意的是,只有在当前cpp文件和包含的文件中定义的函数(例如`Foo`)会作为该编译单元的目标(二进制)版本进行构建,而仅仅声明的函数(例如`Boo`)不会。

链接阶段是C++在每个编译单元中查找声明和调用的阶段,并将其链接到调用的位置。如果找不到对该函数的定义,链接器会报错。同样地,如果找到了相同函数签名(即名称和参数类型)的多个定义,链接器也会报错,因为它认为这是有歧义的,不希望随意选择一个。

这就是你遇到的问题。通过在`mainfile.cpp`中包含`fun.cpp`文件,`fun.cpp`和`mainfile.cpp`都有一个`funct()`的定义,链接器不知道在程序中使用哪一个,因此报错。

解决方法如上面Vaughn所提到的,不要在`mainfile.cpp`中包含带有`funct()`定义的cpp文件,而是将`funct()`的声明放在一个单独的头文件中,并在`mainfile.cpp`中包含该头文件。这样,编译器将得到`funct()`的声明进行处理,链接器将从`fun.cpp`中获得一个`funct()`的定义,并放心地使用它。

所以,我理解得对吗:`mainfile.cpp:main()`中对`funct()`的调用算作`funct()`的定义吗?

我曾经有同样的理解,直到一位同事纠正我:不,事实上,是将`fun.cpp`包含到`mainfile.cpp`中导致了两个`funct()`的定义(一个在`fun.cpp`中,一个在`mainfile.cpp`中)。包含`fun.cpp`会将其内容复制并粘贴到`mainfile.cpp`中,其中包括`funct()`的定义。这就是为什么包含实现文件而不是头文件是不好的做法。: p

0
0 Comments

问题是,如果在程序中两次包含fun.cpp,就会导致fun函数被定义两次,这是无效的。

你不应该包含cpp文件,而应该包含头文件。

头文件应该只包含类的定义。相应的cpp文件将在单独编译时包含函数定义。

fun.hpp:

#include 
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="<

fun.cpp:

#include "fun.hpp"
using namespace std;
void funct(){
    cout<<"enter funct"<

mainfile.cpp:

#include 
#include "fun.hpp"
using namespace std;
int main(int nargin,char* varargin[]){
    cout<<"call funct"<

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

另外,对于某些链接器来说,使用头文件保护可以帮助解决一些问题-搜索#ifndef

我以为多重定义错误是链接器错误而不是编译错误。但也许我弄错了。

是的,但头文件保护与此无关。除非在头文件中做一些疯狂的事情,否则不会有问题。

为什么对于类可以工作而对于函数不行?我不明白函数与类定义有什么特别之处,需要将其放置在cpp文件中。

:类只是一个编译时的实体。如果你定义了一个你从未使用过的类,那么编译器不会创建任何机器代码。然而,非内联函数定义意味着实际上创建机器代码,无论你是否使用它。你只希望将该机器代码存储在一个目标文件中。如果将函数定义放在头文件中,那么无论在哪里包含该头文件,都会意味着创建机器代码。

- 编译器?不是。预处理器,当然是。

0