2011-08-06 40 views
2

我有多個類從一個基類繼承,每個類都在一個單獨的頭。我希望用戶能夠僅包含基類頭,但這需要我將其他派生類包含在基頭中,這會導致某種循環依賴,並且我已經在某處讀取了循環依賴是件壞事。繼承和C++中的多個文件

我的類是一個套接字類,派生類是socket_udp,socket_tcp,socket_raw等等。

我該如何解決循環依賴?也許類設計不好,我不需要不同的類爲不同的套接字類型?我在這裏有點困惑。

謝謝!

+1

爲什麼你需要在派生類中包含派生類?這似乎沒有道理。 –

+0

@Henning Makholm:「我希望用戶只能包含基類頭文件」。有沒有另一種方法來實現這一目標? – Lockhead

+1

我明白你的觀點是,用戶甚至不應該知道派生類存在 - 它們只會在運行時從某個工廠函數返回,但用戶代碼會以多態方式處理它們。如果這不是凹痕,不要理我的評論。 –

回答

8

一個選項是創建一個新的頭文件,其中包含派生基礎對象和基礎對象的所有頭文件。因此只包括那個單一的標題將包括所有其他的標題文件。如果你在這個文件中定義了包含,那麼如果沒有首先包含它的所有父類,就不會包含一個類,那麼你可以避免循環依賴。

+0

這是一個簡單而優秀的解決方案!我真的不敢相信我沒有想到這一點。非常感謝=] – Lockhead

2

可以將#include指令添加到基類頭的底部,以在定義基類後強制包含派生類。

然後,派生類頭將不需要0​​基類頭,打破循環。

+0

我不確定我完全按照。如何防止派生類的頭文件包含基類? – Lockhead

+0

@MisterSir:你是編寫代碼的人,對吧?你只是不要在派生類頭文件中使用'#include',因爲你不需要。 –

2

聽起來好像你正在嘗試創建一個系統,其中socket(raw,udp,tcp)的實現完全隱藏在最終用戶的身上 - 我假設這就是爲什麼你不需要其他頭文件要包含的文件。

一個選擇是讓套接字中的所有方法都是純虛擬的,然後讓每個實現都編譯爲自己的單元,只返回基類的引用。但是,您仍然必須提供一個工廠接口,以便可以將套接字實例化爲正確的類型;

所以有點像;

socket.h中:

class socket { 
     public: 
      virtual void send(std::string& data) = 0; 
      .... 
    }; 
    socket& factory_socket_tcp(some parameters); 
    socket& factory_socket_udp(some parameters); 

socket_tcp.cpp:

class socket_tcp :public socket { 
     public: 
      virtual void send(std::string& data); 
      .... 
    }; 
    socket& factory_socket_tcp(some parameters) { 
     socket &s = socket_tcp(....); 
     return s; 
    } 

或者你可以去一個pImpl (aka Opaque Pointer) as described in this answer here的替代路線 - opaque指針具有含所有人的利益隱藏在cpp文件中的實現細節的,因此沒有任何內容泄漏到.h文件。對於真正的公共接口,這通常是首選選項

2

除非基類定義包含任何原因的派生類的任何信息,否則基類頭文件不應該包含派生類的頭文件。因此,實際上並沒有像你所描述的那樣的循環依賴問題。如果基類定義確實包含來自派生類的一些信息,那麼我認爲你的類層次結構佈局實際上是錯誤的。

但我真的相信你已經表述一個問題,你到底想要做的是不同的事情:創建一個包含所有有趣的(派生)類的頭文件,使應用程序可以方便地只包含這個單獨的頭文件並且每個派生類的聲明都可用。也就是某種摘要頭文件。

而這正是您的問題的建議解決方案。避免混淆基類頭文件:只需創建一個包含每個其他派生類頭文件的單獨文件。通過這種方式,您可以保持與類層次結構相關的文件不受影響,並且還允許應用程序選擇性地包含某些或其他類聲明,而不包含所有這些文件。一些衆所周知的庫,如Qt,使用這種方法。