项目结构如下:
project
--common.h
#pragma once
#define qq(mm) \
int mm_x;
qq(haha)
--aa.cpp
#include <common.h>
int main()
{
return 0;
}
--bb.cpp
#include <common.h>
--CMakeLists.txt
add_library(test SHARED ./aa.cpp ./bb.cpp)
(或者用gcc指令: g++ aa.cpp bb.cpp -o main.exe)
这里宏变量qq就是定义了一个全局整形变量mm_x,
比如在common.h中写一行qq(haha), 预编译时就会被替换成int haha_x;也就是说在头文件中定义了一个全局整形变量haha_x
现在执行cmakelists进行编译,首先回顾一下编译的过程:
每个cpp文件都是一个独立的编译单元,会被编译成一个目标文件,
此处将两个cpp文件共同编成一个动态库,其实是先编译aa.cpp生成test.obj, 再编译bb.cpp, 生成bb.obj, 然后把bb.obj往test.obj上链接,
但是在链接时,会报错multiple definition of 'haha_x’
回顾一下头文件,不是加了pragma once吗,这个头文件内容只会被编译1次,从第二次被包含开始,其内容就不会再被编译了,只会被bb.cpp的源码部分拿来认一认一些声明
那么这里利用宏定义了一个全局变量(严格来说是声明了一个全局变量), 也应该服从pragma once的条件编译规则呀, 第一次被包含,真的声明了haha_x这样一个变量, 但是从第二次开始就不应该再在bb.cpp这个编译单元中再声明一次了,为何链接时还会报重复定义的错误呢?
难道是pragma once有啥问题吗,换#ifndef COMMON_H试试? 但是会发现也无济于事, 依然报重复定义的错
好吧,其实原因是:预编译有三个步骤, 递归包含头文件→宏替换→条件编译, 也即qq(haha)被替换成int haha_x;是优先于条件编译的, 宏定义产生的全局变量,其会直接加入到源码,不受条件编译的限制,
所以在aa.cpp中第一次包含common.h,会声明一个haha_x变量, 在bb.cpp中第二次包含common.h, 也会直接声明一个haha_x,再去看条件编译的内容, 然后两个目标以合并,完了, haha_x这个名字冲突了!
但是如果把使用宏的qq(haha)从函数与类外部移动到函数或类内部, 就可以避免这个问题, 也即其不再是全局变量,自然就不会冲突了
所以,要么别把宏定义的全局变量放在头文件中声明,要么这个头文件只允许被包含一次
采取后者的方法,要求这个头文件真的没啥共用的内容,不被多次包含也是可以的,否则就会导致别人源码不认识一些声明. 那么还不如干脆将其变成一个源文件. 也即
project
--common.h
#pragma once
#define qq(mm) \
int mm_x;
--defines.cpp
#include <common.h>
qq(haha)
--aa.cpp
#include <common.h>
int main()
{
return 0;
}
--bb.cpp
#include <common.h>
--CMakeLists.txt
add_library(test SHARED ./aa.cpp ./bb.cpp ./defines.cpp)
(或者用gcc指令: g++ aa.cpp bb.cpp defines.cpp -o main.exe)