C 類包含問題 重複包含和相互包含

2022-07-18 08:57:09 字數 3540 閱讀 5441

一.重複包含標頭檔案

標頭檔案重複包含,可能會導致的錯誤包括:變數重定義,型別重定義及其他一些莫名其妙的錯誤。c++提供兩種解決方案,分別是#ifndef和#pragma once

#ifndef _some_file_h_                      #pragma once

#define _some_file_h_                      ...//一些宣告語句

...//一些宣告語句

#endif

第一種方式:通過這種預處理實現唯一檢查。預處理首先測試_some_file_h_變數是否未定義,如果_some_file_h_未定義,那麼#ifndef測試成功,跟在#ifndef後面的所有行都被執行,直到發現#endif。相反,如果_some_file_h_已定義,那麼#ifndef指示測試為假,該指示和#endif指示間的**都被忽略。

為了保證標頭檔案在給定的原始檔中只處理一次,首先檢測#ifndef。第一次處理標頭檔案時,測試會成功,因為_some_file_h_還未定義,下一條語句定義了_some_file_h_,那樣的話,如果編譯的檔案恰好又一次包含了該標頭檔案,#ifndef指示會發現_some_file_h_已經定義,並且忽略該標頭檔案的剩餘部分。

當沒有兩個標頭檔案定義和使用同名的預處理器常量時,這個策略相當有效。當有兩個檔案使用同乙個巨集,這個策略就失效了。有兩種方案解決:

1.可以為定義在標頭檔案裡的實體(如類)命名預處理器變數,來避免預處理器變數重名的問題。如,乙個程式只能含有乙個名為sales_item的類,通過使用類名來組成標頭檔案和預處理器變數的名字,可以使得很可能只有乙個檔案將會使用該預處理器變數

2.為了保證巨集的唯一性,可以採用google提供的解決方案,在定義巨集時,巨集名基於其所在的專案源**數的全路徑命名,巨集命名格式為:___h_

在定義#ifndef測試巨集時,巨集名最好採用全大寫字母表示

在定義測試巨集時,最好採用google提供的解決方案

第二種方式:#pragma once

這種方式一般由編譯器提供,只要在標頭檔案的最開始加入這條指令就能夠保證標頭檔案只被編譯一次。#pragma once用來防止某個標頭檔案被多次include,#ifndef方式用來防止某個巨集被多次定義。#pragma once是編譯相關,即在這個編譯系統上使用,但在其它編譯系統上不一定能用,也就是說移植性差,不過現在基本上已經是每個編譯器都有這個定義了。

針對#pragma once,gcc已經取消對其的支援了,而微軟的vc++卻依然支援

如果寫的程式需要跨平台,最好使用#ifndef方式,而避免使用#pragma once方式

二.相互包含問題

ca類包含cb類的例項,而cb類也包含ca類的例項。**如下

實現ca類的定義                        實現cb類的定義        

#include "b.h"                           #include "a.h"               int main()

class ca                              class cb                  

cb instaceb;                           ca instacea;

編譯出錯分析:

ca類定義時使用cb類的定義,cb類定義時使用ca類的定義,遞迴定義

a.h包含了b.h,b.h包含了a.h,也存在遞迴包含的問題

其實,無論是結構體的遞迴定義,還是類的遞迴定義,最後都歸於乙個問題,c/c++採用靜態編譯模型,在程式執行時,結構或類大小都會在編譯後確定。程式要正確編譯,編譯器必須知道乙個結構或結構所占用的空間大小,否則編譯器就會報出奇怪的編譯錯誤。

解法:1.向前宣告實現

實現ca類的定義                        實現cb類的定義

class cb//前向宣告cb類                      #include "a.h"                #include "b.h"

class ca                              class cb                    int main()

public:                               public:                      ca instacea;

int idata;                            int idata;                    return 0;

cb* pinstaceb;                         ca instacea;                }

前向宣告實現方式的主要實現原則

主函式只需要包含b.h就可以,因為b.h中包含了a.h

a.h中不需要包含b.h,但要宣告class cb,在避免死迴圈的同時也成功引用了cb

包含class cb宣告,而沒有包含標頭檔案b.h,這樣只能宣告cb型別的指標,而不能例項化

2.friend宣告定義

實現ca類的定義                        實現cb類的定義

class cb//前向宣告cb類                      #include "a.h"                #include "b.h"

class ca                              class cb                    int main()

public:                               public:                      ca instacea;

friend classcb;  //友元類宣告

int idata;                            int idata;                    return 0;

cb* pinstaceb;                         ca instacea;                }

friend友元宣告實現說明:

主函式只需要包含b.h就可以,因為b.h中包含了a.h

a.h不需要包含b.h,但要宣告class cb,在避免死迴圈的同時也成功引用了cb

class ca包含class cb友元宣告,而沒有包含標頭檔案b.h,這樣只能宣告cb型別的指標,而不能例項化

無論是前向宣告還是friend友元實現,有一點是肯定的,即最多只能有乙個類可以定義例項。同樣標頭檔案包含也是一件很麻煩的事情,再加上標頭檔案中常常出現的巨集定義,各種巨集定義的展開是非常耗時間的

類或結構體遞迴定義實現應遵循兩個原則

1.如果可以不包含標頭檔案,那就不要包含了。這時候前置宣告可以解決問題的。如果使用的僅僅是乙個類的指標,沒有使用這個類的具體物件,也沒有訪問到類的具體成員,那麼前置宣告就可以了。因為指標這一資料型別的大小是特定的,編譯器可以獲知。

2.盡量在cpp檔案中包含標頭檔案,而非在標頭檔案。假設類a的乙個成員是乙個指向類b的指標,在類a的標頭檔案中使用了類b的前置宣告並編譯成功,那麼在a的實現中需要訪問b的具體成員,因此需要包含標頭檔案,那麼我們應該在類a的實現部分包含類b的標頭檔案而非宣告部分。

C 類相互包含

1.兩個類需要相互包含的情景 在觀察者模式中,氣象站的資料送給某些布告牌。氣象站要知道通知哪些布告牌,所以氣象站類至少有乙個布告牌型別的鍊錶。布告牌需要把自己註冊到氣象站類,告訴氣象站類自己已經訂閱訊息,註冊函式需要氣象站類作為形參,才知道自己註冊到哪乙個氣象站。也就是說,在類定義的時候,他們就是需...

c 標頭檔案相互包含問題

c 中標頭檔案互相包含經常會出現編譯錯誤.示例 如下 h ifndef a h define a h include b.h includeusing namespace std class a endif a h a.cpp include a.h a a a a int a getvala vo...

C標頭檔案相互包含的問題

在c 中,關於cpp的標頭檔案互相包含的問題很讓人頭疼,其實我們誰也不願意弄的結構混亂,難以理解,但有時又是有必須的。假定當前有兩個標頭檔案分別為 a.h 和 b.h,內容分別如下 a.h內容為 ifndef a h define a h endif include b.h class a publ...