C++标准,是否允许两个互不可见的类重名?

lnyat 2012-08-16 08:32:55
以下是一个测试程序,编译器是 VS2010

test1.cpp:


class Cs
{
public:
int test()
{
return 1;
}
int test1()
{
return 10;
}
};

int test1()
{
return Cs().test() + Cs().test1();
}


test2.cpp:


class Cs
{
public:
int test()
{
return 2;
}
int test2()
{
return 20;
}
};

int test2()
{
return Cs().test() + Cs().test2();
}


main.cpp:


#include <stdio.h>
#include <stdlib.h>

int test1();
int test2();

int main()
{
printf("%d %d\n", test1(), test2());
system("pause");
return 0;
}


输出结果竟然是的:11 21


疑问:

1、同一个编译模块中,C++标准是否允许两个类重名,或者这个根本不是标准的一部分?
2、出现这样的情况是不是MS链接器的BUG?
3、如果C++标准允许类重名,链接器也没有问题,那这算不算是C++标准的一个缺陷?
...全文
899 51 打赏 收藏 转发到动态 举报
写回复
用AI写文章
51 条回复
切换为时间正序
请发表友善的回复…
发表回复
friendbkf 2015-05-10
  • 打赏
  • 举报
回复
又试了下,如果开了编译器优化的话,即使只用了-O1优化,也可以正常输出了
因为一开了优化,.o文件中就没有内联函数符号了。
第一张图是没有开优化的时候:


下面这张图片是开过优化之后


开过优化之后,输出就正常了。


不过,分析了这么多,还是那么句话:大型项目请使用namespace,还有,同一命名空间下,如果inline同名函数出现的话,其定义必须相同,否则会出现不定的情况。
friendbkf 2015-05-10
  • 打赏
  • 举报
回复
楼主你好,书上说 “对于同一程序的不同文件,如果inline同名函数出现的话,其定义必须相同,否则会出现不定的情况”。
这个不定的情况是指,对于同名的内联函数,链接期,先碰到哪个.o文件,就会只用那个.o文件中的inline函数定义。
由于先碰到哪个.o文件是不定的,所以说输出结果是不定的。
下面我做了一张图,十分清晰地展示了我上面的话。
ri_aje 2012-08-16
  • 打赏
  • 举报
回复
不要再纠结于如何理解看似高深莫测的 undefined behavior,因为这样的行为不受 c++ 标准指导,获得的"知识"也没有广泛的通用性,不同编译器可能不一样,同款编译器不同版本也可能不一样。
ri_aje 2012-08-16
  • 打赏
  • 举报
回复
[Quote=引用 32 楼 的回复:]

编译器在执行某方法时会跳到方法内部,然后再return到原方法中,
由于是非static 方法,所以是在用到时才编译的方法,
你的类名,方法名相同,编译器会认为是同样一个东西, 所以不再重复编译同名方法,
其实你第二个CPP中,调用了第一个类里面的test()方法。
调试结果确实是这样。。。
[/Quote]
这是局限于 vs 的片面结论,比如我用 g++-4.7.0 编译,输出结果就是 11 22。标准说的是 undefined behavior,意思就是编译器自己看着办,因此在这个程序上 vs 和 g++ 可以说都是正确的。只不过这种程序基本是无用的,因为其行为没有确定性。
lnyat 2012-08-16
  • 打赏
  • 举报
回复
[Quote=引用 34 楼 的回复:]
inline是建议,但写在头文件中的函数只要加了inline这个关键字,在被多个文件包含的时候就不会有链接冲突,不管该函数是否被内联。
[/Quote]

是的,不过这跟我提的问题好像没有关系
  • 打赏
  • 举报
回复
[Quote=引用 29 楼 的回复:]

引用 27 楼 的回复:

在类声明中写实现的话则 函数就是inline的函数 ,而inline函数也会存在当前编译单元符号表中。

编译器会保证 同样函数签名的inline函数不会链接冲突。

如果有两个不一样实现的inline函数,那么编译器就就自己决定用哪个实现,所以inline函数都会写在头文件中保证在每个编译单元中实现一致。


inline 是对编译器的一个建议,……
[/Quote]
inline是建议,但写在头文件中的函数只要加了inline这个关键字,在被多个文件包含的时候就不会有链接冲突,不管该函数是否被内联。
nightkids_008 2012-08-16
  • 打赏
  • 举报
回复
这涉及到编译的时候common块问题,你可以搜索一下。
jiangshi061 2012-08-16
  • 打赏
  • 举报
回复
编译器在执行某方法时会跳到方法内部,然后再return到原方法中,
由于是非static 方法,所以是在用到时才编译的方法,
你的类名,方法名相同,编译器会认为是同样一个东西, 所以不再重复编译同名方法,
其实你第二个CPP中,调用了第一个类里面的test()方法。
调试结果确实是这样。。。
ri_aje 2012-08-16
  • 打赏
  • 举报
回复
[Quote=引用 30 楼 的回复:]
引用 28 楼 的回复:
1、谢谢

2、说是bug不恰当,可不可以认为是设计不周全的地方?

3、
引用 4 楼 的回复:
并不是我有意要这么写的,只是我想到了一个可能会遇到的情况,所以写了上面的测试程序:

[/Quote]
2.可以吧。只不过要设计周全了,可能会很困难。


假如一个项目中,两个人无意中写了两个重名的类,会发生什么情况,出现未定义行为?

是的。


标准或者编译器不可能解决所有问题,但它应该尽量避免出现不必要的问题,上面这个问题是否是标准或者编译器力所能及的?

标准没有按你希望的方式提供解决方案,不过标准有提供 namespace,此项设计恰恰就是为了解决名字冲突的问题。这也是为什么多人合作的大工程,都建议使用 namespace 的原因。实际上,我认为比起指定繁琐的规定去理清当重复定义出现时怎样解决,namespace 是更合理的解决方案。显然标准还是很成熟的。
lnyat 2012-08-16
  • 打赏
  • 举报
回复
[Quote=引用 28 楼 的回复:]

引用楼主 的回复:
1、同一个编译模块中,C++标准是否允许两个类重名,或者这个根本不是标准的一部分?
2、出现这样的情况是不是MS链接器的BUG?
3、如果C++标准允许类重名,链接器也没有问题,那这算不算是C++标准的一个缺陷?

1.不允许。不过主楼的程序涉及三个不同的 translation units,所以和同一个 translation unit 是否允许重名没有关系。标……
[/Quote]

1、谢谢

2、说是bug不恰当,可不可以认为是设计不周全的地方?

3、
[Quote=引用 4 楼 的回复:]
并不是我有意要这么写的,只是我想到了一个可能会遇到的情况,所以写了上面的测试程序:

假如一个项目中,两个人无意中写了两个重名的类,会发生什么情况,出现未定义行为? 标准或者编译器不可能解决所有问题,但它应该尽量避免出现不必要的问题,上面这个问题是否是标准或者编译器力所能及的?
[/Quote]
lnyat 2012-08-16
  • 打赏
  • 举报
回复
[Quote=引用 27 楼 的回复:]

在类声明中写实现的话则 函数就是inline的函数 ,而inline函数也会存在当前编译单元符号表中。

编译器会保证 同样函数签名的inline函数不会链接冲突。

如果有两个不一样实现的inline函数,那么编译器就就自己决定用哪个实现,所以inline函数都会写在头文件中保证在每个编译单元中实现一致。
[/Quote]

inline 是对编译器的一个建议,而非面向类的使用者;用户并不需要关注使用的函数是不是 inline 的

这个问题跟是否 inline 无关;虽然函数写在头文件中,编译时不照样是 #include 到 cpp 中吗?
ri_aje 2012-08-16
  • 打赏
  • 举报
回复
[Quote=引用楼主 的回复:]
1、同一个编译模块中,C++标准是否允许两个类重名,或者这个根本不是标准的一部分?
2、出现这样的情况是不是MS链接器的BUG?
3、如果C++标准允许类重名,链接器也没有问题,那这算不算是C++标准的一个缺陷?
[/Quote]
1.不允许。不过主楼的程序涉及三个不同的 translation units,所以和同一个 translation unit 是否允许重名没有关系。标准相关的定义叫 one definition rule (ODR),c++11 3.2/5
There can be more than one definition of a class type (Clause 9), enumeration type (7.2), inline function with external linkage (7.1.2), class template (Clause 14), non-static function template (14.5.6), static data member of a class template (14.5.1.3), member function of a class template (14.5.1.1), or template specialization for which some template parameters are not specified (14.7, 14.5.5) in a program provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements. Given such an entity named D defined in more than one translation unit, then
— each definition of D shall consist of the same sequence of tokens; and ...

主楼程序中的 test 函数在 test1.cpp 和 test2.cpp 中的 sequence of tokens 是不一样的,因为一个是 return 1; 另一个是 return 2;
因此,主楼的程序违背了 ODR 原则,属于 C++ 经典的 undefined behavior.

2.这里不存在 bug 可言。因为所谓的 bug 是说程序的行为与正确的行为不一致,但是 c++ 标准说主楼程序的行为是 undefined,因此没有所谓正确的行为,因此也没有 bug 可言。

3.显然不是 c++ 的缺陷。c++ 对此有清晰的定义,叫做 undefined behavior。简而言之,就是不要写这样的程序,属于标准认证的自找麻烦的程序。
  • 打赏
  • 举报
回复
在类声明中写实现的话则 函数就是inline的函数 ,而inline函数也会存在当前编译单元符号表中。

编译器会保证 同样函数签名的inline函数不会链接冲突。

如果有两个不一样实现的inline函数,那么编译器就就自己决定用哪个实现,所以inline函数都会写在头文件中保证在每个编译单元中实现一致。
Flammable_ice 2012-08-16
  • 打赏
  • 举报
回复
补充下说明。对于C++重要特点多态性。书上是这样解释的,为了写N个功能相似函数,C语言的做法是写函数名不同的函数以便实现不同的功能,而C++把功能相似的函数用同一个函数名让其代码看起来更好的表达其中的含义,这样C++就添加了多态这个在C里完全没有的概念。如果楼主不会使用虚函数,不会用重载,那么楼主还是停留在C语言的概念而没有真正认识到C++的特性。
最后总结一下。不管是重载函数还是虚函数,本来函数可以不同,但是功能相似,那么在C++里面让其代码更易懂,就用同一个函数名实现不同功能。
lnyat 2012-08-16
  • 打赏
  • 举报
回复
[Quote=引用 24 楼 的回复:]

引用 22 楼 的回复:
引用 21 楼 的回复:

引用 20 楼 的回复:
引用 18 楼 的回复:

我给楼主提供一条思路。因为如果以楼主这样在test1.cpp与test2.cpp2个CPP文件里面定义不加任何修饰的text()函数,会使test1.cpp里面的text()函数在链接时覆盖掉test2.cpp里面的text(),于是就得出楼主的结果。你可以声明text()为……
[/Quote]

谢谢,^_^

那是好几年前的事情了
Flammable_ice 2012-08-16
  • 打赏
  • 举报
回复
[Quote=引用 22 楼 的回复:]
引用 21 楼 的回复:

引用 20 楼 的回复:
引用 18 楼 的回复:

我给楼主提供一条思路。因为如果以楼主这样在test1.cpp与test2.cpp2个CPP文件里面定义不加任何修饰的text()函数,会使test1.cpp里面的text()函数在链接时覆盖掉test2.cpp里面的text(),于是就得出楼主的结果。你可以声明text()为虚函数(virtual text……
[/Quote]
无法解析的外部符号。这是你的text声明了没有定义的结果。
还有就是你最好查查virtual的用法。虚函数这一章还是比较重要的一块,它是实现运行时函数多态性的关键。还有从 printf("%d %d\n", test1(), test2());你是学了C语言正在学习C++的童鞋,C++的三大特点是多态性 封装性 继承性 。这个是区别于C的重要特性哦。
小班得瑞 2012-08-16
  • 打赏
  • 举报
回复
这样的问题我觉得在事先就该规划好,防止隐患吧
lnyat 2012-08-16
  • 打赏
  • 举报
回复
[Quote=引用 21 楼 的回复:]

引用 20 楼 的回复:
引用 18 楼 的回复:

我给楼主提供一条思路。因为如果以楼主这样在test1.cpp与test2.cpp2个CPP文件里面定义不加任何修饰的text()函数,会使test1.cpp里面的text()函数在链接时覆盖掉test2.cpp里面的text(),于是就得出楼主的结果。你可以声明text()为虚函数(virtual text())这样有可能会得到楼主想……
[/Quote]

这个跟链接器的实现细节有关,将 test 的实现注释,并查看链接错误:

没加virtual时,两个test符号相同

1>test1.obj : error LNK2019: 无法解析的外部符号 "public: int __thiscall Cs::test(void)" (?test@Cs@@QAEHXZ),该符号在函数 "int __cdecl test1(void)" (?test1@@YAHXZ) 中被引用
1>test2.obj : error LNK2001: 无法解析的外部符号 "public: int __thiscall Cs::test(void)" (?test@Cs@@QAEHXZ)

其中一个加了virtual后,可以看出链接符号产生了差异

1>test1.obj : error LNK2001: 无法解析的外部符号 "public: virtual int __thiscall Cs::test(void)" (?test@Cs@@UAEHXZ)
1>test2.obj : error LNK2019: 无法解析的外部符号 "public: int __thiscall Cs::test(void)" (?test@Cs@@QAEHXZ),该符号在函数 "int __cdecl test2(void)" (?test2@@YAHXZ) 中被引用
Flammable_ice 2012-08-16
  • 打赏
  • 举报
回复
[Quote=引用 20 楼 的回复:]
引用 18 楼 的回复:

我给楼主提供一条思路。因为如果以楼主这样在test1.cpp与test2.cpp2个CPP文件里面定义不加任何修饰的text()函数,会使test1.cpp里面的text()函数在链接时覆盖掉test2.cpp里面的text(),于是就得出楼主的结果。你可以声明text()为虚函数(virtual text())这样有可能会得到楼主想要的结果。
//都声明为虚函数……
[/Quote]
不知道楼主学没学过虚函数? 对于同样一个函数,加了virtual就可以实现多态了。就是同一个函数多种状态。楼主的意思是加了virtual那两个text是完全不一样的函数?
lnyat 2012-08-16
  • 打赏
  • 举报
回复
[Quote=引用 18 楼 的回复:]

我给楼主提供一条思路。因为如果以楼主这样在test1.cpp与test2.cpp2个CPP文件里面定义不加任何修饰的text()函数,会使test1.cpp里面的text()函数在链接时覆盖掉test2.cpp里面的text(),于是就得出楼主的结果。你可以声明text()为虚函数(virtual text())这样有可能会得到楼主想要的结果。
//都声明为虚函数 结果是12 22.
//……
[/Quote]

因为加了 virtual 后函数符号改变了,相当于函数名不同就和 test1 和 test2 没有链接问题一样
加载更多回复(29)

64,641

社区成员

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

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