C++ 重定义的一些问题
前言
首先从一个问题引入,先看一个遇到 LINK2005 问题的项目,由一个头文件与两个源文件组成,内容分别为
//a.h
#pragma once
int a;
//a.cpp
#include "a.h"
//main.cpp
#include "a.h"
#include<iostream>
using namespace std;
int main() {
cout << 3;
}
这是简化后的项目,该项目报错如图
于是产生了疑惑,为什么已经使用 #pragma once 确保头文件只被 include 一次,但仍然重定义。
pragma once 与 #ifndef 的作用
众所周知,#pragma once 与 #ifndef 作用类似,都是用于确保头文件只被引用一次,比如下面的一个由两个头文件与一个源文件组成的项目
//a.h
int a;
//b.h
#include "a.h"
//main.cpp
#include "b.h"
#include "a.h"
#include<iostream>
using namespace std;
int main() {
cout << 3;
}
此时报错如图
因为在引入 b.h 文件时,编译器已经为 a.h 中的 a 定义过,因此再次引用 a.h 时发生重定义错误,该错误在给 a.h 文件加上 #pragma once 后解决。
LNK2005 问题
但是为什么即使加了 #pragma once 还是会出现前言中的错误呢,因为前言中的错误与 C2086 不同,LNK2005 源于链接时的重定义错误。因为在编译 a.cpp 已经给 a.h 中的 a 定义过,在编译 main.cpp 后又给 a.h 中的 a 再次定义,因此在将 a.obj 与 main.obj 链接时产生错误。这就要考虑到 C++ 中变量的声明与定义与函数的不同。
变量的声明与定义
定义:指为变量分配内存空间
声明:只是告知编译器变量的类型与名字
在 C++ 中,只有
extern int a;
是声明变量的形式,除此之外都是变量的定义,因此前文 a.h 中的 int a;会导致重定义问题。
解决方法
解决方法有二,
1、在源文件中定义变量,即在 int a;前加 extern 前缀。
2、如果一定要在头文件中初始化变量,则可在变量前加 static 前缀,避免其被重定义。