我在几年前参加了一个入门课程后试图重新学习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()'的多重定义”。在将其声明为友元函数时,我使用了错误的语法吗?
答案 0 :(得分:56)
这是一个高度简化但有希望相关的视图,说明在C ++中构建代码时会发生什么。
C ++在以下不同阶段分割生成机器可执行代码的负载 -
预处理 - 这是您可能正在使用的任何宏 - #define
等扩展的地方。
编译 - 将每个cpp文件以及该文件中的所有#include
d文件直接或间接(一起称为编译单元)转换为机器可读对象代码。
这是C ++还检查所有定义的函数(即包含{
}
中的正文的地方,例如
void Foo( int x){ return Boo(x); })
以有效的方式引用其他功能。
它的做法是坚持你在调用它之前至少提供这些其他功能的声明(例如void Boo(int);
),以便它可以检查你是否正确地调用它。这可以直接在调用它的cpp文件中完成,也可以通常在包含的头文件中完成。
请注意,只有与此cpp和包含文件中定义的函数对应的机器代码才会构建为此编译单元的对象(二进制)版本(例如Foo),而不是仅仅声明的那些(例如Boo)。
链接 - 这是C ++寻找在每个编译单元中声明和调用的内容并将其链接到调用它的地方的阶段。现在,如果找不到此函数的定义,则链接器会放弃并输出错误。类似地,如果它找到相同函数签名的多个定义(基本上是它所采用的名称和参数类型),它也会因为它认为它不明确并且不想任意选择一个而出错。
后者就是你的情况。通过执行#include
fun.cpp
文件,fun.cpp
和mainfile.cpp
都定义为funct()
,并且链接器不知道要使用哪个funct()
你的程序,并抱怨它。
上面提到的Vaughn修复程序是不在mainfile.cpp
中包含funct()
定义的cpp文件,而是将mainline.cpp
的声明移到单独的头文件中并包含在funct()
。通过这种方式,编译器将获得funct()
的声明,并且链接器将从fun.cpp
获得{{1}}的一个定义,并将自信地使用它。
答案 1 :(得分:24)
问题是如果你在程序的两个地方包含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
。
答案 2 :(得分:0)
您在 #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 工作。
答案 3 :(得分:-1)
发生此问题的原因是您正在调用 fun.cpp 而不是 fun.hpp 。因此,c ++编译两次查找func.cpp定义,并抛出此错误。
请把您的main.cpp
文件的第3行从#include "fun.cpp"
更改为#include "fun.hpp"