函数有多个定义的错误
函数有多个定义的错误
我在几年前上过一门入门课程,现在正在尝试重新学习C++,但是遇到了一些基本问题。我目前的问题出现在尝试使用友元函数时。以下是我的代码,分为两个文件。
第一个文件:
// fun.cpp #includeusing 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()'"。在声明为友元函数时,我使用的语法是否有误?
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
问题是,如果在程序中两次包含fun.cpp,就会导致fun函数被定义两次,这是无效的。
你不应该包含cpp文件,而应该包含头文件。
头文件应该只包含类的定义。相应的cpp文件将在单独编译时包含函数定义。
fun.hpp:
#includeclass 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文件中。
:类只是一个编译时的实体。如果你定义了一个你从未使用过的类,那么编译器不会创建任何机器代码。然而,非内联函数定义意味着实际上创建机器代码,无论你是否使用它。你只希望将该机器代码存储在一个目标文件中。如果将函数定义放在头文件中,那么无论在哪里包含该头文件,都会意味着创建机器代码。
- 编译器?不是。预处理器,当然是。