美文网首页C/CPLUS
C++<第三十八篇>:如何防止头文件被重复引入

C++<第三十八篇>:如何防止头文件被重复引入

作者: NoBugException | 来源:发表于2022-02-15 20:49 被阅读0次

在 C++ 项目中,经常会发生头文件重复引入导致冲突问题。事实上,一个完整的 C++ 项目由多个代码文件组成,根据后缀的不同,大致可以分成两个部分:
(1).h 文件:又称“头文件”,用于存放常量、函数的声明部分、类的声明部分;
(2).cpp 文件:又称“源文件”,用于存放变量、函数的定义部分,类的实现部分;
但是,常常会重复导入.h文件,导致重定义的错误,本文主要是为了解决.h文件重复导入的问题。

C++ 多文件编程中,多次 #include 导致重复引入的演示代码如下:

A.h

class A
{
public:
    A() {}
    void say();
};

A.cpp

#include "A.h"
#include <iostream>

using namespace std;

void A::say()
{
    cout << "AAAAAAAAAAA" << endl;
}

B.h

#include "A.h";

class B
{
private:
    A a;
public:
    void setA(A a);
    A getA();
    void say();
};

B.cpp

#include "B.h"
#include <iostream>

using namespace std;

void B::setA(A a)
{
    this->a = a;
}

A B::getA()
{
    return a;
}

void B::say()
{
    cout << "BBBBBBBBB" << endl;
}

main.cpp

#include <iostream>
#include"A.h"
#include"B.h"

using namespace std;

int main()
{
    A* a = new A();
    B* b = new B();
    b->setA(*a);
    b->getA().say();

    return 0;
}

以上代码运行之后会报如下错误:

image.png

如上错误所示,以上代码 A 类重新定义了,原因是在 main.cpp 文件中引入了 A.h 和 B.h,而在 B.h 中有引入了 A.h,相当于 A.h 被引入了两次,所以会报错。

问题的解决方案有三种,分别是:使用宏定义避免重复引入、使用#pragma once避免重复引入、使用_Pragma操作符。

(1)使用宏定义避免重复引入

在实际多文件开发中,我们往往使用如下的宏定义来避免发生重复引入:

#ifndef _NAME_H
#define _NAME_H
//头文件内容
#endif

其中,_NAME_H 是宏的名称。需要注意的是,这里设置的宏名必须是独一无二的,不要和项目中其他宏的名称相同。

之前演示的代码是 A.h 文件被重复引入,只要修改下 A.h,就可以解决重复引入的问题,修改后的 A.h 代码如下:

#ifndef _A
#define _A
class A
{
public:
    A() {}
    void say();
};
#endif
(2)使用#pragma once避免重复引入

我们还可以使用 #pragma one 指令,将其附加到指定文件的最开头位置,则该文件就只会被 #include 一次。

#ifndef 是通过定义独一无二的宏来避免重复引入的,这意味着每次引入头文件都要进行识别,所以效率不高。但考虑到 C 和 C++ 都支持宏定义,所以项目中使用 #ifndef 规避可能出现的“头文件重复引入”问题,不会影响项目的可移植性。

和 ifndef 相比,#pragma once 不涉及宏定义,当编译器遇到它时就会立刻知道当前文件只引入一次,所以效率很高。但值得一提的是,并不是每个版本的编译器都能识别 #pragma once 指令,一些较老版本的编译器就不支持该指令(执行时会发出警告,但编译会继续进行),即 #pragma once 指令的兼容性不是很好。

目前,几乎所有常见的编译器都支持 #pragma once 指令,甚至于 Visual Studio 2017 新建头文件时就会自带该指令。可以这么说,在 C/C++ 中,#pragma once 是一个非标准但却逐渐被很多编译器支持的指令。

如何使用? 只要在被重复文件的顶部添加 #pragma once 即可。

#pragma once

class A
{
public:
    A() {}
    void say();
};
(3)用_Pragma操作符

C99 标准中新增加了一个和 #pragma 指令类似的 _Pragma 操作符,其可以看做是 #pragma 的增强版,不仅可以实现 #pragma 所有的功能,更重要的是,_Pragma 还能和宏搭配使用。

修改后的代码如下:

_Pragma("once")

class A
{
public:
    A() {}
    void say();
};
总结:

(1) #pragma once 和 _Pragma("once") 可算作一类,其特点是编译效率高,但可移植性差(比如:编译器不支持,会发出警告,但不会中断程序的执行)
(2)而 #ifndef 的特点是可移植性高,编译效率差。

需要知道的是:在某些场景中,考虑到编译效率和可移植性,#pragma once 和 #ifndef 经常被结合使用来避免头文件被重复引入。比如说:

#pragma once
#ifndef _A
#define _A

class A
{
public:
    A() {}
    void say();
};
#endif

[本章完...]

相关文章

  • C++<第三十八篇>:如何防止头文件被重复引入

    在 C++ 项目中,经常会发生头文件重复引入导致冲突问题。事实上,一个完整的 C++ 项目由多个代码文件组成,根据...

  • #ifndef/#define/#endif

    “头文件中的 #ifndef/#define/#endif 防止该头文件被重复引用” “被重复引用”是指一个头文件...

  • 文件声明

    文件声明 头文件防卫式声明 头文件的防卫式声明可以防止头文件被反复引入。如果一个头文件被反复引入,在编译的过程中会...

  • 编程规范 - 头文件

    【摘自《 Google C++ 编程规范》】 所有头文件都应该使用 #define 防止头文件被多重包含(mult...

  • 头文件(.h)中#ifndef/#define/#endif的用

    先说下作用吧:头文件中的 #ifndef/#define/#endif 是为了防止该头文件被重复引用。 何为头文件...

  • Header Guard

    Header Guard:防止头文件被重复包含Header Guard形式: 重复包含的问题: 预处理速度慢 重复...

  • 一些题

    1、头文件中的ifndef/define/endif的作用?防止该头文件被重复引用2、#include

  • #ifndef/#define/#endif使用详解

    想必很多人都看过“头文件中的 #ifndef/#define/#endif 防止该头文件被重复引用”。但是...

  • C/C++前向声明

    C/C++前向声明 头文件循环引用 避免引入头文件

  • C++ Boolan第一周

    1. 关于头文件 做了预处理后可以防止被多次重复引用

网友评论

    本文标题:C++<第三十八篇>:如何防止头文件被重复引入

    本文链接:https://www.haomeiwen.com/subject/lbwxkrtx.html