C++ - Shared Header Causes Multiply Defined Symbol Error
今天遇到一个多重定义问题,自己写了个小程序实验了下,
一个头文件head.h定义了一个字符串常量。两个cpp,main.cpp,source.cpp 都include head.h文件
Complie能过Link的时候报错了,vc 报的错误是:error LNK2005: "char const * const pchar" (?pchar@@3PBDB) already defined in main.obj。
头文件head.h代码如下:
思考一:#ifndef _HEAD_J_H
#define _HEAD_J_H
const char * pchar = "123456";
#endif _HEAD_J_H
为什么我已经加了#ifndef了还是会导致 重定义,#ifndef #define ....#endif的作用不是防止重定义么?
跟小菊同学讨论之后,他说#ifndef防止重定义是在单个obj内的,即如果单个cpp文件多次包含某一头问题只会编译一次,但是如果有多个cpp文件,#ifndef只会限制每个cpp文件内的头文件不会包含多次,几个cpp文件之间是不会有影响的。我查了《windows核心编程》这本书里有讲编译链接生成exe或dll的过程。编译过程中每个cpp都会编译生成一个对应obj文件,链接过程中将每个obj连接起来生成一个可执行文件。我又查了《c++primer》关于#ifndef的内容,里面提到了是为了防止编译过程,看清楚是编译过程中防止头文件被多重定义,所以#ifndef确实只会限制单个obj文件里头文件不被包含多次;
思考二:
我将上面的head.h的代码改为如下的却complie和link都成功了
#ifndef _HEAD_J_H #define _HEAD_J_H const char ch = 'd'; #endif _HEAD_J_H
说好的重定义呢?怎么又没了呢?修改之前和修改之后唯一的区别是变量ch是const类型,和pchar只是指向const 内容的指针,却不是const 类型指针, 于是乎我又将代码改为如下:
#ifndef _HEAD_J_H #define _HEAD_J_H //void func(); const char * const pchar = "123456"; #endif _HEAD_J_H
将指针也变为const类型,就又成功了。照理说 既然我这么定义在头文件里 肯定是global变量了,在main.cpp里定义了一次在source.cpp里定义了一次,在链接obj的时候发现有两个 同样的变量,这个时候必然会报重定义错误。但是改为const类型之后为什么不报错了呢,我去查了《c++primer》里面有关const内容的,里面有一段话是大致是这样写的:
C++中的const对象默认为文件的局部变量,与其他变量不同,不特殊申明的话,在全局作用域内的const变量定义该对象文件的局部变量,此变量只存在于那个文件中,不能被其他文件访问,其他文件访问需要加extern
看了上面的解释,相信大家也都知道了原因了,既然作为局部变量了,几个cpp文件link的时候当然不会出错了。这也是const的特性吧,这是一种解决 头文件里变量重定义的方法;
思考三:
我们大部分在头文件里定义 变量 基本都是常量,大家可以用#define 宏定义来解决问题,宏定义 内容是存储在代码段的,在编译之前做文本替换。宏定义的一个确定 就是 类型检查 ,各有优弊吧,大家自行选择;
思考四:
在网上搜索资料的时候还发现了一种方法,就是将cpp文件改为.c文件,这样就会按照语言编译,在C语言中,如果遇到多个 比如说是 int i 吧,会将第一个作为定义,其他的都作为声明,所以不会出错;
总结:一般来说C++里不建议你在头文件里定义变量的,所以大家还是能移则移到cpp吧,然后用extern来支持多个文件访问;