为什么在两个C++源文件中声明名称相同但是内容不同的类,不会引起连接异常呢

为什么在两个C++源文件中声明名称相同但是内容不同的类,不会引起连接错误呢?
在Visual Studio中建立一个工程,包含两个源文件 O1.cpp 和 O2.cpp

O1.cpp代码如下:
class A {
int i;
};

void f1() {
A a;
}

O2.cpp代码如下:
class A{
};

void f2() {
A a;
}

main()函数在另外的文件中
我的问题是,为什么这样一个工程进行build,可以顺利通过呢?Class A被定义了两次,为什么在Link时不会出现多重定义错误呢
知道的同志烦请解释,不甚感激

------解决方案--------------------
如果你两个函数定义成一样的名字就会出错了,比如都是f或者都是g。

大致上说link是将机器码导入到一起的过程,如果一个函数有两个定义那就有两份代码,link的时候就会冲突,但是单纯的类定义,编译器不会生成任何代码,所以有可能不出错。
------解决方案--------------------
用过include吧?include常做的一件事就是把你的.h文件中的class inline到.cpp文件中。
如果用include没有产生链接冲突,自己在.cpp中加上自然也不应该有链接冲突。
------解决方案--------------------
至于为什么函数会链接冲突而类没有,原因很简单:函数是代码,会占用内存;而类只是一个关于内存表示的抽象定义而已,类定义本身没有实际的内存。

------解决方案--------------------
>我就是想不通,C++编译器为什么非要实现成这个样子,为什么它不限制在所有的文件中,
>只要类名相同,那么定义也必须一致,这不是很符合逻辑吗
我想主要是技术问题吧。你怎么判断定义是否一致?
我记得《C++语言的设计和演化》有一小段提到了这个,但是想不起来了……书算是白读了。
------解决方案--------------------
探讨
我就是想不通,C++编译器为什么非要实现成这个样子,为什么它不限制在所有的文件中,只要类名相同,那么定义也必须一致,这不是很符合逻辑吗

------解决方案--------------------
去查一查C++的标准文档吧,虽然我自己也没有查过,但是可以推测:C++是把cpp文件作为编译单元的,编译期间只要同一个编译单元内部没有冲突就可以通过编译,之后C++使用C的连接器进行连接,此时如果各个编译单元间相互引用的元素有冲突会出现连接错误。对于你给出的场景,两个类都分别在自己的编译单元中,在编译单元编译过程中没有冲突(因为每个cpp文件中都只有唯一的类定义),连接时两个编译单元之间又没有引用对方定义的类,所以也不会出现问题,不过有特殊情况会导致能通过编译链接却会在运行时出现错误。例如:
//a.cpp
class A
{
public:
short sValue1;
short sValue2;
};

A* GetInstance()
{
A* pNew = new A;
pNew->sValue1 = 1;
pNew->sValue2 = 1;
return pNew;
}

//b.cpp
#include <iostream>
using namespace std;

class A
{
public:
int iValue;
};

A* GetInstance();

int main(int argc, char* argv[])
{
A* p = GetInstance();
cout << p->iValue << endl;
return 0;
}

以上代码也可以通过编译,但是b.cpp文件使用了类似hack的方式修改了A的定义,使得编译器在两个编译单元中对A进行了不同的解释,从而输出了
0x10001对应的整数值(实际上把两个short组合成了一个int),例子如此给出多少有些故意为之的巧合,实际中通常会造成内存越界访问等等极不容易被发现的错误。
其实你仔细考虑就会发现,在不同的编译单元中定义同名不同义的代码元素几乎总能通过编译甚至连接,但是几乎也一定会造成运行时的莫名其妙的错误。所以我个人觉得不是万不得已不应该写出这样的代码。
------解决方案--------------------
本来出现这种错误的概率也不大,故意在程序中这样写的人更是少之又少。
在这种情况下,是否需要花那么大力气在各个编译单元之间详细检查所引用的所有符号的内容一致性,而且说不定还需要牺牲编译器和连接器之间的独立性——这至少是个见仁见识的问题。
------解决方案--------------------
你还问了编译器为何这么做,我想编译器没有义务做这种事情,这是程序员的义务。编译器从技术上可以做到,但是那将导致obj文件要导出所有外部完全用不到的信息,连接时还必须检查所有obj导出的这些信息并判断冲突,这样的结果可想而知,不仅编译生成的文件增大很多,编译链接的效率将会极大的降低,如果你愿意每天等待编译器花掉你大部分的时间去做编译链接,那或许编译器的实现者会考虑这么做。
------解决方案--------------------
楼主的问题很好啊
你的2个cpp是单独编译的, 如果再加上一个包含main的文件应该更能说明问题
main中如果只include其中一个文件,就不会链接另外一个文件(我的理解是不是错了?),当然就没有冲突,如果你include2个文件,肯定会有重定义的错误

另外,不鼓励include cpp, 还是要使用h头文件的好