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

Seth_Zhujy 2008-06-15 09:12:06
在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时不会出现多重定义错误呢
知道的同志烦请解释,不甚感激
...全文
2576 25 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
25 条回复
切换为时间正序
请发表友善的回复…
发表回复
night_legend 2008-06-17
  • 打赏
  • 举报
回复
所获颇多!
wsqshz 2008-06-16
  • 打赏
  • 举报
回复
C++ Primer第四版上有这样的话:(出现在12.1.4)
“在一个给定的源文件中,一个类只能被定义一次。如果在多个文件中定义一个类,那么每个文件中的定义必须是完全相同的”

注意:此处说的是 在多个文件中定义一个类。而你定义的是两个类,只是类名相同,但处于不同的作用域,故而互不相干。
Maxwell 2008-06-16
  • 打赏
  • 举报
回复
如果你两个函数定义成一样的名字就会出错了,比如都是f或者都是g。

大致上说link是将机器码导入到一起的过程,如果一个函数有两个定义那就有两份代码,link的时候就会冲突,但是单纯的类定义,编译器不会生成任何代码,所以有可能不出错。
Seth_Zhujy 2008-06-16
  • 打赏
  • 举报
回复
那请你指出我在9楼的回帖具体哪里是错的,不要总是下结论。

至于结果和理解的问题,那是个人风格,我喜欢弄清楚现象背后的本质
taodm 2008-06-16
  • 打赏
  • 举报
回复
同样的实验,我根据结果来修正“理解”。
你试图用“理解”来强套结果。
结果已经证明你理解错了。
taodm 2008-06-16
  • 打赏
  • 举报
回复
"两个类都已经拥有了非内联函数,经试验,这样子还是不会出错 "


你这2个函数又不存在冲突,你想到哪获得“重定义”?

Seth_Zhujy 2008-06-16
  • 打赏
  • 举报
回复
O1.cpp
class A {
public:
int g();
};

int A::g() {
return 2;
}


O2.cpp
class A {
public:
int f();
};

int A::f() {
return 1;
}


两个类都已经拥有了非内联函数,经试验,这样子还是不会出错

另外,taodm 关于符号表的说明我很感兴趣,这正是我欠缺的地方。不知能否详细说明一下,或者推荐一些资料,谢过先
taodm 2008-06-16
  • 打赏
  • 举报
回复
“类的定义”只是增加了一个符号表,根本就没有什么可link的。
类拥有非内联的实现时,才会导致link上的问题。
Seth_Zhujy 2008-06-16
  • 打赏
  • 举报
回复
To wsqshz:
你关于作用域的解释很准确。没有不敬的意思,不过这个真的跟作用域有关吗?我查找过一些资料,似乎都没有提到一个类会被局限在文件作用域。我举下面的例子说明我的意思:

如果在O1.cpp和O2.cpp中定于两个同名的函数
O1.CPP

void f() {

}

O2.CPP

void f() {
int a = 1;
}

这样在程序在进行连接时会有 多重定于错误 ,因为定义在O1中的f()是一个全局函数,属于全局作用域,定义在O2中的f()也是一个全局函数,属于全局作用域,这样全局作用域中就会有两个函数,从而引起连接错误

现在我是在O1.cpp和O2.cpp中定义了两个同名的类
O1.CPP

class A {

};

O2.CPP

class A {
public:
void f1(){}
};

我并没有在什么资料上看到说,类的定义会局限在文件中(也就是拥有文件作用域,就像const常量和static常量),我觉得O1中的class A是属于全局作用域的,O2中的class A也是属于全局作用域的。同样的一个全局作用域,定义了两个同名的类型,这种情况不就像上面函数的例子一样吗?为什么不会引起连接错误呢

如果我的理解有误,请指正


To zmlovelx:
如果不同的人要使用相同的名字,那么应该使用名字空间去解决
定义不同的名字空间,在其中定义相同的名字,包括类名,都不会引起混淆和错误
所以我觉得这个不是原因
K行天下 2008-06-16
  • 打赏
  • 举报
回复
楼主的问题很好啊
你的2个cpp是单独编译的, 如果再加上一个包含main的文件应该更能说明问题
main中如果只include其中一个文件,就不会链接另外一个文件(我的理解是不是错了?),当然就没有冲突,如果你include2个文件,肯定会有重定义的错误

另外,不鼓励include cpp, 还是要使用h头文件的好
帅得不敢出门 2008-06-16
  • 打赏
  • 举报
回复
世界上写程序的那么多,如果 限制在所有的文件中,只要类名相同,那么定义也必须一致 那么做大项目的时候怎么办??
那么多人总有不同的人写不同的东西用上相同的名字, 那这时候怎么办呢....
gentlelotus 2008-06-16
  • 打赏
  • 举报
回复
你还问了编译器为何这么做,我想编译器没有义务做这种事情,这是程序员的义务。编译器从技术上可以做到,但是那将导致obj文件要导出所有外部完全用不到的信息,连接时还必须检查所有obj导出的这些信息并判断冲突,这样的结果可想而知,不仅编译生成的文件增大很多,编译链接的效率将会极大的降低,如果你愿意每天等待编译器花掉你大部分的时间去做编译链接,那或许编译器的实现者会考虑这么做。
晨星 2008-06-16
  • 打赏
  • 举报
回复
本来出现这种错误的概率也不大,故意在程序中这样写的人更是少之又少。
在这种情况下,是否需要花那么大力气在各个编译单元之间详细检查所引用的所有符号的内容一致性,而且说不定还需要牺牲编译器和连接器之间的独立性——这至少是个见仁见识的问题。
gentlelotus 2008-06-16
  • 打赏
  • 举报
回复
去查一查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),例子如此给出多少有些故意为之的巧合,实际中通常会造成内存越界访问等等极不容易被发现的错误。
其实你仔细考虑就会发现,在不同的编译单元中定义同名不同义的代码元素几乎总能通过编译甚至连接,但是几乎也一定会造成运行时的莫名其妙的错误。所以我个人觉得不是万不得已不应该写出这样的代码。
晨星 2008-06-16
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 wsqshz 的回复:]
我就是想不通,C++编译器为什么非要实现成这个样子,为什么它不限制在所有的文件中,只要类名相同,那么定义也必须一致,这不是很符合逻辑吗
[/Quote]
C++不是“故意”要做成这个样子的,只是它没考虑那么多。它是不想费那个力气去好到那种程度,而不是故意要做得这么坏。

它本来就是来自于C,编译器和连接器是基于符号的。
iambic 2008-06-16
  • 打赏
  • 举报
回复
>我就是想不通,C++编译器为什么非要实现成这个样子,为什么它不限制在所有的文件中,
>只要类名相同,那么定义也必须一致,这不是很符合逻辑吗
我想主要是技术问题吧。你怎么判断定义是否一致?
我记得《C++语言的设计和演化》有一小段提到了这个,但是想不起来了……书算是白读了。
iambic 2008-06-16
  • 打赏
  • 举报
回复
至于为什么函数会链接冲突而类没有,原因很简单:函数是代码,会占用内存;而类只是一个关于内存表示的抽象定义而已,类定义本身没有实际的内存。
iambic 2008-06-16
  • 打赏
  • 举报
回复
用过include吧?include常做的一件事就是把你的.h文件中的class inline到.cpp文件中。
如果用include没有产生链接冲突,自己在.cpp中加上自然也不应该有链接冲突。
wsqshz 2008-06-16
  • 打赏
  • 举报
回复
我就是想不通,C++编译器为什么非要实现成这个样子,为什么它不限制在所有的文件中,只要类名相同,那么定义也必须一致,这不是很符合逻辑吗


作用域规则的含义:不同的作用域中相同的名字互不相干。
你只要理解为什么要有作用域,问题就迎刃而解。
chwhaiwei 2008-06-16
  • 打赏
  • 举报
回复
同意Maxwell说的
加载更多回复(5)

65,201

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧