C++中標頭檔相互包含的幾點問題

來源:互聯網
上載者:User

  閱讀: 222 評論: 0 作者: mini資料庫 發表於 2009-05-29 20:00 原文連結

一、類嵌套的疑問

C++標頭檔重複包含實在是一個令人頭痛的問題,前一段時間在做一個簡單的資料結構示範程式的時候,不只一次的遇到這種問題。假設我們有兩個類A和B,分別定義在各自的有檔案A.h和B.h中,但是在A中要用到B,B中也要用到A,但是這樣的寫法當然是錯誤的:
class B;

class A
{
      public:
          B b;
};

class B
{
      public:
          A a;
};
因為在A對象中要開闢一塊屬於B的空間,而B中又有A的空間,是一個邏輯錯誤,無法實現的。在這裡我們只需要把其中的一個A類中的B類型成員改成指標形式就可以避免這個無限延伸的怪圈了。為什麼要更改A而不是B?因為就算你在B中做了類似的動作,也仍然會編譯錯誤,表面上這僅僅上一個先後順序的問題。
      為什麼會這樣呢?因為C++編譯器自上而下編譯源檔案的時候,對每一個資料的定義,總是需要知道定義的資料的類型的大小。在預先聲明語句class B;之後,編譯器已經知道B是一個類,但是其中的資料卻是未知的,因此B類型的大小也不知道。這樣就造成了編譯失敗,VC++6.0下會得到如下編譯錯誤:
      error C2079: 'b' uses undefined class 'B'
將A中的b更改為B指標類型之後,由於在特定的平台上,指標所佔的空間是一定的(在Win32平台上是4位元組),這樣可以通過編譯。

二、不同標頭檔中的類的嵌套

      在實際編程中,不同的類一般是放在不同的相互獨立的標頭檔中的,這樣兩個類在相互引用時又會有不一樣的問題。重複編譯是問題出現的根本原因。為了保證標頭檔僅被編譯一次,在C++中常用的辦法是使用條件編譯命令。在標頭檔中我們常常會看到以下語句段(以VC++6.0自動產生的標頭檔為例):

#if !defined(AFX_STACK_H__1F725F28_AF9E_4BEB_8560_67813900AE6B__INCLUDED_)
#define AFX_STACK_H__1F725F28_AF9E_4BEB_8560_67813900AE6B__INCLUDED_
      //很多語句……
#endif

其中首句#if !defined也經常做#ifndef,作用相同。意思是如果沒有定義過這個宏,那麼就定義它,然後執行直到#endif的所有語句。如果下次在與要這段代碼,由於已經定義了那個宏,因此重複的代碼不會被再次執行。這實在是一個巧妙而高效的辦法。在高版本的VC++上,還可以使用這個命令來代替以上的所有:
      #pragma once
它的意思是,本檔案內的代碼只被使用一次。

      但是不要以為使用了這種機制就全部搞定了,比如在以下的代碼中:

//檔案A.h中的代碼
#pragma once

#include "B.h"

class A
{
      public:
          B* b;
};

//檔案B.h中的代碼
#pragma once

#include "A.h"

class B
{
      public:
          A* a;
};

這裡兩者都使用了指標成員,因此嵌套本身不會有什麼問題,在主函數前面使用#include "A.h"之後,主要編譯錯誤如下:
      error C2501: 'A' : missing storage-class or type specifiers
仍然是類型不能找到的錯誤。其實這裡仍然需要前置聲明。分別添加前置聲明之後,可以成功編譯了。代碼形式如下:

//檔案A.h中的代碼
#pragma once

#include "B.h"

class B;

class A
{
      public:
          B* b;
};

//檔案B.h中的代碼
#pragma once

#include "A.h"

class B;

class B
{
      public:
          A* a;
};

這樣至少可以說明,標頭檔包含代替不了前置聲明。有的時候只能依靠前置聲明來解決問題。我們還要思考一下,有了前置聲明的時候標頭檔包含還是必要的嗎?我們嘗試去掉A.h和B.h中的#include行,發現沒有出現新的錯誤。那麼究竟什麼時候需要前置聲明,什麼時候需要標頭檔包含呢?

三、兩點原則

      標頭檔包含其實是一想很煩瑣的工作,不但我們看著累,編譯器編譯的時候也很累,再加上標頭檔中常常出現的宏定義。感覺各種宏定義的展開是非常耗時間的,遠不如自訂函數來得速度。我僅就不同標頭檔、源檔案間的句則結構問題提出兩點原則,僅供參考:

第一個原則應該是,如果可以不包含標頭檔,那就不要包含了。這時候前置聲明可以解決問題。如果使用的僅僅是一個類的指標,沒有使用這個類的具體對象(非指標),也沒有訪問到類的具體成員,那麼前置聲明就可以了。因為指標這一資料類型的大小是特定的,編譯器可以獲知。

第二個原則應該是,盡量在CPP檔案中包含標頭檔,而非在標頭檔中。假設類A的一個成員是是一個指向類B的指標,在類A的標頭檔中使用了類B的前置聲明並便宜成功,那麼在A的實現中我們需要訪問B的具體成員,因此需要包含標頭檔,那麼我們應該在類A的實現部分(CPP檔案)包含類B的標頭檔而非聲明部分(H檔案)。

 

from http://hi.baidu.com/shilyx/blog/item/c1e3f7f277e29811b17ec5f2.html

  發表評論

新聞頻道:分析:Google手機Nexus One能否挑戰蘋果iPhone

推薦連結:Windows 7專題發布

網站導航:部落格園首頁  個人首頁  新聞  社區  博問  快閃記憶體  知識庫

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.