如果我在头文件中声明一个全局变量并将其包含在两个.cpp文件中,则链接器会给出一个错误,指出该符号是多重定义的。 我的问题是,为什么只对某些类型的对象(例如int)而不是其他对象(例如enum)发生这种情况?
我使用的测试代码如下:
test.h
#ifndef TEST_HEADER
#define TEST_HEADER
namespace test
{
int i_Test1 = -1;
int i_Test2 = -1;
};
#endif // TEST_HEADER
class1.h
#ifndef CLASS_1_HEADER
#define CLASS_1_HEADER
class class1
{
public:
void count();
};
#endif //CLASS_1_HEADER
class1.cpp
#include <iostream>
#include "class1.h"
#include "test.h"
void class1::count()
{
std::cout << test::i_Test1 << std::endl;
}
class2.h
#ifndef CLASS_2_HEADER
#define CLASS_2_HEADER
class class2
{
public:
void count();
};
#endif //CLASS_2_HEADER
class2.cpp
#include "class2.h"
#include <iostream>
#include "test.h"
void class2::count()
{
std::cout << test::i_Test2 << std::endl;
}
的main.cpp
#include "class1.h"
#include "class2.h"
int main(int argc, char** argv)
{
class1 c1;
class2 c2;
c1.count();
c2.count();
return -1;
}
使用以下代码构建此代码:
g++ main.cpp class1.cpp class2.cpp -o a
产生以下输出:
ld:致命:符号
test::i_Test1' is multiply-defined: (file /var/tmp//ccwWLyrM.o type=OBJT; file /var/tmp//ccOemftz.o type=OBJT); ld: fatal: symbol
test :: i_Test2'是多重定义的: (文件/var/tmp//ccwWLyrM.o type = OBJT;文件/var/tmp//ccOemftz.o 类型= OBJT); ld:致命:文件处理 错误。没有输出写入 collect2:ld返回1退出状态
如果我更改test.h文件,如下所示:
test.h(带枚举)
#ifndef TEST_HEADER
#define TEST_HEADER
namespace test
{
enum val
{
i_Test1 = 5,
i_Test2
};
//int i_Test1 = -1;
//int i_Test2 = -1;
};
#endif // TEST_HEADER
我没有得到“多重定义”错误,程序给出了所需的输出:
5
6
答案 0 :(得分:21)
那是因为枚举不是对象 - 它们是类型。如果所有定义都满足一些限制(由所谓的一个定义规则(ODR)总结),则可以在整个程序中多次定义类类型(类,结构,联合)和枚举。最重要的两个是
您的枚举定义满足ODR的所有条件。因此,这是有效的,没有理由让链接器/编译器呻吟(实际上,对于违反ODR,编译器也不需要发出消息 - 大多数属于所谓的无需诊断规则,某些违规行为也会导致未定义的行为)。
但是,对于每个非内联函数和对象,这些必须仅定义一次。乘法定义会导致虚假错误,就像您的情况一样。要解决此问题,只需将声明放入头文件(使用“extern”而不使用初始化程序),并将一个定义放入其中一个.cpp文件中(省略“ extern“然后,或者放一个初始化器。如果它是一个const对象,你仍然需要”extern“,因为每个默认的const变量都有内部链接,否则不会导出符号)。