C/C++ ifndef与头文件重复包含解析

ifndef

一个预处理指令,作用是判断一个符号是否存在,或者是不存在,与之对应的还有一个ifdef、ifndef一般用于解决头文件重复包含的问题。

符号

符号一般指的是,函数定义的名称,类名,宏名称,这些都可以称为符号。

#ifndef Y
#define Y	//如果Y不存在,则重新定义Y的符号
#else 
			//如果Y存在
#endif

ifndef解决include的重复包含关系

头文件A.h的内容

#pragma once
#include <iostream>
void A()
{
	std::cout << "A" << std::endl;
}

创建一个A.h头文件,然后进行引用这个头文件,其实引用的这个步骤在编译器看来就是代码替换,比如在一个cpp中,引入一个A.h头文件,其实就是吧A.h的代码拷贝一份到cpp文件,代码例子(因为编译器的编译步骤为:预编译-->等价的头文件替换成代码-->代码编译)

1 引用A.h文件

#include "A.h"

//以下是别的代码
int main(void)
{
	Api* pOne = Factory::createApi(1);
	Api* pTwo = Factory::createApi(2);
	pOne->test("哈哈哈");
	pTwo->test("嘻嘻嘻");
	system("pause");
	return 0;
}

2 经过编译器预处理之后,把include "A.h”进行替换(与文本宏替换等价)

#include <iostream>
void A()
{
	std::cout << "A" << std::endl;
}

//以下是别的代码
int main(void)
{
	Api* pOne = Factory::createApi(1);
	Api* pTwo = Factory::createApi(2);
	pOne->test("哈哈哈");
	pTwo->test("嘻嘻嘻");
	system("pause");
	return 0;
}

3 递归循环头文件等价代码的替换,一直到iostream头文件的代码替换,最终编译。

考虑问题

1 如果一个cpp文件中,代码出现重复包含头文件的操作,为什么会出现这种情况,因为写代码的时候可能一个头文件下面写了很多代码,可能有时候忘记自己添加过一个相同的头文件了,后面又重复包含了一个头文件,这样可以编译过去,但是重复替换给编译器带来了编译速度慢的问题

#include "A.h"
//代码....
#include "A.h"
//代码....
#include "A.h"

2 头文件代码被编译器重复替换之后,出现了三段相同的代码,因为刚刚已经引用了3次include "A.h"头文件,最终导致编译速度慢

#include <iostream>
void A()
{
	std::cout << "A" << std::endl;
}
//代码....
#include <iostream>
void A()
{
	std::cout << "A" << std::endl;
}
//代码....
#include <iostream>
void A()
{
	std::cout << "A" << std::endl;
}

3 解决问题,在include "A.h"头文件添加#ifndef预处理指令,此时就可以解决重复包含的问题

#ifndef AAA
#define AAA
#include <iostream>
void A()
{
	std::cout << "A" << std::endl;
}
#endif

最终会被替换成如下形式,如果AAA符号没有被定义,则重新定义AAA符号,那么下面两段代码还会继续判断AAA是否被定义,这时候AAA已经被前面一段进行定义了,所以这一段代码就会无效,编译器将不会进行等价代码替换,这时候就解决了头文件重复包含了

//第一段代码
#ifndef AAA//如果AAA不存在
#define AAA//如果AAA不存在,则重新定义AAA
#include <iostream>
void A()
{
	std::cout << "A" << std::endl;
}
#endif


//代码....


//以下两段代码是无效的
//第二段代码
#ifndef AAA//以下代码将变灰,不再有效,因为AAA已经被重新定义了
#define AAA
#include <iostream>
void A()
{
	std::cout << "A" << std::endl;
}
#endif


//代码....
//第三段代码
#ifndef AAA//以下代码将变灰,不再有效,因为AAA已经被重新定义了
#define AAA
#include <iostream>
void A()
{
	std::cout << "A" << std::endl;
}
#endif

ifndef重命名解决

假设定义了两个头文件,include "A.h"与include "B.h",里面定义相同的预处理指令ifndef AAA,此时A.h是ifndef AAA,B.h也是ifndef AAA

include "A.h"的代码

#ifndef AAA
#define AAA
#include <iostream>
void A()
{
	std::cout << "A" << std::endl;
}

#endif

include "B.h"的代码

#ifndef AAA
#define AAA
#include <iostream>
void B()
{
	std::cout << "B" << std::endl;
}

#endif

此时经过编译器把头文件进行等待替换之后,第二段代码变成无效的代码,第二段是B其实是有效的代码,此时因为符号重名了,所以就变的无效了

//第一段代码
#ifndef AAA
#define AAA
#include <iostream>
void A()
{
	std::cout << "A" << std::endl;
}

#endif
//第二段代码
//因为符号AAA已经定义了,所以这一段代码无效,编译器不会进行编译
#ifndef AAA
#define AAA
#include <iostream>
void B()
{
	std::cout << "B" << std::endl;
}

#endif

所以,ifndef后面跟着的符号是有特殊要求规则的,因为头文件的名称是固定的,一个头文件对应一个名称,所以可以让头文件的名称作为ifndef的符号名称,此时符号就不会重名了,一般的命名方法是__头文件名称_H,如 ifndef __A_H,其中A是头文件的文件名,H是头文件的后缀名

include "A.h"的内容

#ifndef __A_H
#define __A_H
#include <iostream>
void A()
{
	std::cout << "A" << std::endl;
}

#endif

include "B.h"的内容


#ifndef __B_H
#define __B_H
#include <iostream>
void B()
{
	std::cout << "B" << std::endl;
}

#endif

所以经过处理之后,代码的写法应该是如下形式


//第一段代码有效
#ifndef __A_H
#define __A_H
#include <iostream>
void A()
{
	std::cout << "A" << std::endl;
}

#endif
//第二段代码也有效,也会进行编译
#ifndef __B_H
#define __B_H
#include <iostream>
void B()
{
	std::cout << "B" << std::endl;
}

#endif

此时就可以利用ifndef完美解决头文件重复包含的问题了

总结

1 ifndef可以有效解决头文件重复包含问题

2 include头文件其实就是等价的代码拷贝

3 ifndef的符号名必须是独立的,没有定义过的,否则会造成有效代码没有进行编译

4 ifndef的一般用法为 __头文件名称_H

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一个卖卡的小男孩~小曾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值