C++,那些可爱的小陷阱(一)

此系列是为那些读过TC++PL或者具有类似水平的同学准备的,作为系列的第一篇以及有趣的热身,我们来看一个链接问题:

D1.cpp

#include  < stdio.h >
struct  X {
    X(
int );
    X(
int int );
};
X::X(
int   =   0 int   =   0 ) { printf( " 1\n " ); }
class  D:  public  X {
}; 
int  minus( int  a, int  b)
{
    D d;
    
return  a - b;
}

D2.cpp 

#include  < stdio.h >
struct  X {
    X(
int );
    X(
int int );
};
X::X(
int   =   0 ) { printf( " 2\n " ); }
class  D:  public  X {
};
int  add( int  a, int  b)
{
    D d;
    
return  a + b;
}

用来执行的main.cpp:

#include  < stdio.h >
int  minus( int  a, int  b);
int  add( int  a, int  b);
int  main()
{
    add(
1 , 2 );
    minus(
1 , 2 );
}

 

将以上三个cpp文件分别编译并链接成一个应用程序,在运行之前,请先猜个结果。然后运行。

好吧,我想你看到了答案。在你开始思考为什么会是这样的之前,请再做一件事,将D1.cpp和D2.cpp两个文件的内容完全交换,然后重新编译运行一次。

 

请原谅上面写的比较混乱且没有给出每一步的结果,因为使用VS的C++编译器,这个结果是不确定的。然而毫无疑问,两次运行的结果将是不同的!

在我这里,第一次显示了

2
2

第二次显示了
1
1

OMG,文件居然影响了程序结果!所以请不要总是相信编译器,这个例子来自C++标准,它用以说明一个重要的准则:One Defination Rule,简写ODR

ODR在C++标准中被解释为:

1.任何编译单元都不能包含变量、函数、枚举、类或者模板的定义一次以上。

2.所有程序必须且只能包含一次其中用到的所有非内联函数和对象。

3.在需要类的完整定义的编译单元中,类的定义必须且只能出现一次。

4.(好bt的一条啊,恰好这一条可以解释我们的程序)包括类、枚举、类模板......(具体有哪些请自己看spec)在内的一些定义可以在一个程序中出现多次,但是必须满足以下条件:

    (1)所有定义的token序列必须相同(token你可以认为就是有效的语言要素,出了空白、换行注释之类的)

    (2)所有的命名查找必须指向同一个实体,也就是说,你不能搞一些命名空间 typedef之类的,让这些相同的token表示不同的意义

    (3)所有运算符必须表示同一个重载

    (4)对于你要定义的实体中的所有带默认参数的函数,默认参数必须满足以上三条

    (5)对于类定义,构造函数中调用的基类构造函数必须是同一个

总而言之,这个第四条的意思就是不同的定义之间不能有任何歧义

 

所以按照标准,应该无法通过链接器,这里似乎VC++实现的不是很理想,产生了一个未定义行为,既没有给出警告也没有报错。晚上回去看看g++的表现,也欢迎知道更多细节的朋友指点。

 

PS.g++似乎做了跟VC++相同的事情,表现基本一致。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值