【C++進階之路】C++防止頭文件被重複引入的3種方法!

在以前咱們詳細介紹了 C 語言中如何使用宏定義(#ifndef / #define / #endif)來有效避免頭文件被重複 #include,此方式在 C++ 多文件編程中也很經常使用。編程

舉個例子,以下是一個 C++ 項目,其內部含有 school.h 和 student.h 這 2 個頭文件以及 main.cpp 源文件,其各自包含的代碼爲:學習

//student.hspa

class Student {視頻

    //......blog

};ip

//school.h資源

#include "student.h"開發

class School {get

    //......編譯器

private:

    Student stu[50];

};

//main.cpp

#include "student.h"

#include "school.h"

int main() {

    //......

    return 0;

}

運行此項目會發現,編譯器報「Student 類型重定義」錯誤。這是由於在 school.h 文件中已經 #include 了一次 "student.h",而在 main.cpp 主程序又同時 #include 了 "school.h" 和 "student.h",即 Student 類的定義被引入了 2 次,C++不容許同一個類被重複定義。

有小夥伴可能想到,既然 School.h 文件中已經引入了 Student 類,那去掉 main.cpp 主程序引入的 student.h 文件不就能夠了嗎?這樣確實能夠避免重複引入 Student 類,但此方式並不適用於全部「重複引入」的場景。

C++ 多文件編程中,處理「屢次 #include 致使重複引入」問題的方式有如下 3 種。

————————

1) 使用宏定義避免重複引入

在實際多文件開發中,咱們每每使用以下的宏定義來避免發生重複引入:

#ifndef _NAME_H

#define _NAME_H

//頭文件內容

#endif

其中,_NAME_H 是宏的名稱。須要注意的是,這裏設置的宏名必須是獨一無二的,不要和項目中其餘宏的名稱相同。

當程序中第一次 #include 該文件時,因爲 _NAME_H 還沒有定義,因此會定義 _NAME_H 並執行「頭文件內容」部分的代碼;當發生屢次 #include 時,由於前面已經定義了 _NAME_H,因此不會再重複執行「頭文件內容」部分的代碼。

也就是說,咱們能夠將前面項目中的 student.h 文件作以下修改:

#ifndef _STUDENT_H

#define _STUDENT_H

class Student {

    //......

};

#endif

雖然該項目 main.cpp 文件中仍 #include 了 2 次 "student.h",但鑑於 _STUDENT_H 宏只能定義一次,因此 Student 類也僅會定義一次。再次執行該項目會發現,其能夠正常執行。

 

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 只能做用於某個具體的文件,而沒法向 #ifndef 那樣僅做用於指定的一段代碼。

這裏仍之前面的 "student.h" 文件爲例,將其內容修改成:

#pragma once

class Student {

    //......

};

再次運行項目,一樣能夠正常執行。

 

3) 使用_Pragma操做符

C99 標準中新增長了一個和 #pragma 指令相似的 _Pragma 操做符,其能夠看作是 #pragma 的加強版,不只能夠實現 #pragma 全部的功能,更重要的是,_Pragma 還能和宏搭配使用。

有關 _Pragma 操做符更多的功能和用法,本節不作詳細講解,這裏僅介紹如何用 _Pragma 操做符避免頭文件重複引入。

當處理頭文件重複引入問題時,能夠將以下語句添加到相應文件的開頭:

_Pragma("once")

好比,將該語句添加到前面項目中 student.h 文件中的開頭位置,再次執行項目,其能夠正常執行。

事實上,不管是 C 語言仍是 C++,爲防止用戶重複引入系統庫文件,幾乎全部庫文件中都採用了以上 3 種結構中的一種,這也是爲何重複引入系統庫文件編譯器也不會報錯的緣由。

 

總結

本節介紹了 3 種避免頭文件被重複引入的方法,其中 #pragma once 和 _Pragma("once") 可算做一類,其特色是編譯效率高,但可移植性差(編譯器不支持,會發出警告,但不會中斷程序的執行);而 #ifndef 的特色是可移植性高,編譯效率差。讀者可根據實際狀況,挑選最符合實際須要的解決方案。

除非對項目的編譯效率有嚴格的要求,強烈推薦讀者選用第一種解決方案,即採用 #ifndef / #define / #endif 組合解決頭文件被重複引入。

另外在某些場景中,考慮到編譯效率和可移植性,#pragma once 和 #ifndef 常常被結合使用來避免頭文件被重複引入。好比說:

#pragma once

#ifndef _STUDENT_H

#define _STUDENT_H

class Student {

    //......

};

#endif

當編譯器能夠識別 #pragma once 時,則整個文件僅被編譯一次;反之,即使編譯器不識別 #pragma once 指令,此時仍有 #ifndef 在發揮做用。


 

最後,無論你是轉行也好,初學也罷,進階也可,若是你想學編程~

【值得關注】個人 C/C++編程學習交流俱樂部【點擊進入】

問題答疑,學習交流,技術探討,還有超多編程資源大全,零基礎的視頻也超棒~

相關文章
相關標籤/搜索