今天用VC编译一个别人的项目时总是提示重复定义。发生问题的头文件虽然被引用多次,但是有#pragma once#ifndef的保护,不应该出现这种情况。

仔细检查后发现该项目启用了预编译,并且目录下有一个已经生成好的.pch文件。将之删除后重新编译通过。

程序源代码大致为

// utility.h
#ifndef __CYKER__
#define __CYKER__
#pragma once
/* struct/function definition */
#endif

// stdafx.h
#include "utility.h"

// main.cpp
#include "stdafx.h"
#include "utility.h"

推测宏保护机制在这种情况下失效的原因为,编译main.cpp时引用了stdafx.h文件,因为目录下已经有一个编译好的.pch文件,所以直接从中得到struct/function的定义。接下来引用了utility.h文件,因为该文件未被包含过,#ifndef和#pragma once都不起作用,所以又定义了一次struct/function,导致重复定义。

也就是说宏保护机制并不总是那么安全的,如果你当前的程序目录中包含中间文件。

Note. 什么是预编译?

预编译就是对常用的且基本保持不变的头文件进行预先编译,得到预编译头文件(如VC下的.pch文件)。.pch文件类似于.obj文件。当编译器启用了预编译,并且编译到stdafx.h时,若目录下存在编译好的.pch文件并且stdafx.h没有改变,则stdafx.h不会被再次编译,而是直接从.pch文件中寻找目标代码。从而加快了整个工程的编译速度。

所以应该把常用的且基本保持不变的头文件(如utility.h)放入stdafx.h中,在.cpp文件中只需要引用stdafx.h(而不是utility.h),从而实现预编译。注意stdafx.h这个名字不是固定的。为了编译stdafx.h中的代码,我们需要stdafx.cpp。这个文件一般只有一句:

#include “stdafx.h"

其作用相当于把stdafx.h所包含的代码放到.cpp文件中,从而进行编译(.h文件是不能被编译的)。

Update. 其实解决这个问题有个最简单的方法,编译的时候选择Rebuild (some_project)就行了。VC会自动删除中间文件,重新生成。