假设以下代码:
/******* mylib.h *******/
#include <iostream>
class foo
{
public:
void test()
{
#ifdef MYDEF
::std::cout<<"ZERO\n";
#else
::std::cout<<"ONE\n";
#endif
}
};
/******* module.h *******/
#include "mylib.h"
class module
{
public:
void test();
}
/******* module.cpp *******/
#include "module.h"
class module
{
public:
void test()
{
foo foo_;
foo_.test();
}
};
/******* main.cpp *******/
#define MYDEF
#include "mylib.h"
#include "module.h"
void main()
{
foo foo_;
module mod_;
foo_.test();
mod_.test();
}
使用
g++
,我独立编译每个实现文件:module.cpp
--> module.o
main.cpp
--> main.o
然后我继续将它们链接在一起,创建
a.out
可执行文件:module.o
+ main.o
= a.out
以上所有内容都很好,执行
a.out
打印出:问题:
考虑到
module.o
和 main.o
必须对 foo::test()
有不同的定义,因为 main.cpp
定义了 MYDEF
而 module.cpp
没有定义 MYDEF
,链接器必须选择保留这两个定义之一,因为,正如我们在上面看到的,所有对 foo::test()
的调用都表达相同的行为。链接器如何决定使用哪个 foo::test()
定义?我可以影响这个决定吗?如果我的库还有十几个函数都依赖于 MYDEF
宏怎么办,是否可以保证所有这些函数都会看到相同的 MYDEF
宏定义状态?利用这种行为打破了单一定义规则,但是还应该如何以更好的方式在foo::test()
中实现例如条件编译的调试打印?
不可能对此进行可靠的影响。
使用不同设置的
MYDEF
编译两个翻译单元是 ODR 违规,因为您的程序包含类 foo
的两个不相同的定义。该程序是 IFNDR(格式错误,无需诊断)并且实际上具有未定义的行为。
这也不仅仅是一个理论上的问题。编译器可以任意内联调用
test
函数,或者在假设 test
具有与翻译单元中可见的相同行为的情况下编译其他函数。
因此,即使您可以影响链接器选择两种实现之一,程序可能仍然不会按照您的预期运行。