今天在练习 非友元式运算符重载的时候出现了一个问题(using 什么的省略)
//A.h
class A{
public:
void fun();
}
//A.cpp
#include "A.h"
void A::fun(){
}
ostream &operator<<(ostream &out, A &x){
out<<"test";
return out;
}
//main.cpp
#include "A.h"
int main(){
A a;
cout<<a;
return 0;
}
错误提示没有A类型的<<重载, 这是因为main中的<<根本没有重载成功。
为什么没有重载成功?因为我们包含A.h的时候,根本没有把A.cpp中的类容包含进来。
但是我们却可以成功使用a.fun(),为什么呢,A.cpp中的类容不是没有包含进来吗?
这是因为域作用符::发挥了作用,当编译器编译A.cpp的时候,把A.h中的声明和它的实现连接到了一起,虽然我们没有包含A.cpp,但是A.h中的类已经和它的实现连在一起包含了。但是operator<<就没这么幸运了,他可没人领到main中去。
那么我们会想,我把operator<<放到A.h中不就行了?
是的,虽然operator<<这时候被main.cpp包含了,但是同时,由于A.cpp中的#include "A.h",使operator<<既在main.cpp中定义又在A.cpp中定义,这样重复定义会出错。
当一个源码中有太多函数时,我们希望用几个源码来分别存放这些函数,那么会有多个CPP文件进行编译,这时很可能出现重复编译的问题。
例子1
//文件A A.h
#ifndef A_H
#define A_H
#include <iostream>
using namespace std;
class F{
};
fun(){
cout<<'A';
}
#endif
//文件B B.cpp
//文件C C.cpp
#include "A.h"
int main(){
fun();
return 0;
}
这时候 fun()函数正常运行,因为我们在C文件中用到fun()在A.h中定义过,符合要求。(注意,此时B文件是空的。)
下面,因为我们在头文件中声明了F类,自然要新建一个CPP定义F,那么需要在B中包含A
//文件B B.cpp
#include "A.h"
我们暂时不定义具体类容,此时再编译,出现错误:
错误1error LNK2005: "void __cdecl fun(void)" (?fun@@YAXXZ) 已经在 B.obj 中定义
这是因为,虽然我们在C中只包含了一个头文件,看似只定义了一次fun(),但是编译器还要编译另一个CPP,B,而B中因为包含了A,所以B再次定义了fun(),这样fun定义了两次,编译器就会罢工。
解决办法有两个:
1)将fun的定义放在文件C中
2)A.h中只声明fun,将fun的定义放到B中
总结:声明可以重复,但定义不可以,头文件中尽量只声明,不要具体实现。