2008-12-23 88 views
13

我試圖創建適當的頭文件,其中不包含太多的其他文件以保持它們的乾淨並加快編譯時間。轉發基類的聲明

我遇到的,而這樣做的兩個問題:

  1. 在基類向前聲明不起作用。

    class B; 
    
    class A : public B 
    { 
    
        // ... 
    } 
    
  2. 對STD類的前向聲明不起作用。

    namespace std 
    { 
        class string; 
    } 
    
    class A 
    { 
        string aStringToTest; 
    } 
    

如何解決這些問題?

回答

24

您無法解決的第一個問題。

第二個問題是沒有什麼關係的標準庫類。這是因爲你將類的實例聲明爲你自己類的成員。

這兩個問題都是由於編譯器必須能夠從其定義中找出類的總大小。

但是,編譯器可以計算出指向某個類的指針的大小,即使它尚未具有完整的定義。所以在這種情況下可能的解決方案是在消費類中有一個指針(或引用)成員。在基類案件

沒有太大的幫助,因爲你不會得到一個「是」的關係。

也不是值得做這樣的事情std::string。首先,它應該是一個字符緩衝區的方便包裝,可以讓你從簡單的事情上進行內存管理。如果你持有一個指向它的指針,爲了避免包含頭部,你可能會考慮一個好主意。其次(正如在評論中指出的那樣),std::stringstd::basic_string<char>的類型定義。所以你需要轉發declare(然後使用),相反,到那時候事情變得非常模糊和難以閱讀,這是另一種成本。是不是真的值得嗎?

+3

事實上,即使他使用了字符串指針或引用,TomWij也會遇到問題。他犯了錯誤,認爲std :: string是一個類,事實並非如此。它是模板basic_string <>的typedef,並且根本無法進行前向聲明。 – 2008-12-23 21:26:46

+4

@Shmoopty:準確地說,它不能被轉發聲明,因爲模板參數的數量在標準中是未定義的。實現可以將其他模板參數添加到語言所需的參數。 – 2008-12-23 21:38:05

2

在這兩種情況下,編譯器需要知道類型的大小。因此,前向聲明是不夠的。基類可以添加成員或需要虛擬表。字符串成員將需要增加類的大小來存儲STL字符串類的大小。

正向聲明STL類往往是不可取的,因爲實現通常包括加快編制明確模板實例。

3

你太艱苦設法解決的東西,實際上不是一個問題。使用你需要的頭文件,並減少 - 在哪裏可能 - 對它們的要求。但是,不要試圖把它帶到極端,因爲你會失敗。

在某些情況下,PIMPL成語可能對您有所幫助,但不在此處。

1

對於你的基類,你需要有完整的類型定義,而不僅僅是一個聲明。派生類型頭文件需要#include它們的基類的頭文件。

對於std名稱空間中的類,你必須包括正確的頭文件 - <字符串>在這種情況下 - 然後做3兩件事:

  1. 完全限定的類型:的std :: string aStringToTest

  2. 將一個使用聲明僅用於 類型:using std :: string;

  3. 爲 std命名空間使用聲明:using namespace std;

7

正如埃裏克回答過,你不能在任何的情況下,因爲編譯器需要知道類的大小使用前聲明。

只能在一組操作使用前向聲明:

  • 宣佈採取聲明的正向類作爲參數或者返回它
  • 聲明成員指針或引用向前聲明的類
  • 功能
  • 宣佈前聲明的類型的靜態變量在類定義

你不能用它來

  • 聲明給定類型的一個成員屬性(編譯器要求的尺寸)
  • 定義或創建的類型的對象或將其刪除
  • 呼叫之類的任何靜態或構件的方法或訪問任何構件或靜態屬性

(我也不曾忘記任何?)

考慮到,如果宣告auto_ptr是不一樣的聲明原始指針,因爲auto_ptr研究所當超出範圍並且刪除需要完整聲明類型時,antiation會嘗試刪除指針。如果您使用auto_ptr來保存前向聲明類型,則必須提供析構函數(即使爲空),並在完整的類聲明被發現後對其進行定義。

還有一些其他的微妙之處。當你轉發聲明一個類時,你告訴編譯器它將是一個類。這意味着它不能成爲另一種類型的enumtypedef。這就是你所得到的問題,當你嘗試轉發聲明std::string,因爲它是一個模板的特定實例的類型定義:

typedef basic_string<char> string; // aproximate 

要轉發聲明字符串,你就需要轉發聲明basic_string模板,然後創建typedef。問題在於標準沒有說明模板需要的參數的數量,它只是表明如果它需要多個參數,其餘的參數必須具有默認類型,以便上面的表達式編譯。這意味着沒有標準的方式來轉發宣告模板。

如果你要轉發申報非標準模板另一方面(非STL,這是)你可以,只要你知道的參數個數做到這一點:

template <typename T, typename U> class Test; // correct 
//template <typename T> class Test; // incorrect even if U has a default type 

template <typename T, typename U = int> class Test { 
    // ... 
}; 

最後,羅迪給你的建議是:儘可能地向前宣佈,但假設必須包括一些事情。

2

>似乎前向聲明對於基類和stl類是沒用的。

更正... 對於基類和對象成員,前向聲明是INAPPROPRIATE。 (它不是「無用的」,它是「不適用的」。)

當聲明基類爲另一個類的基類時,必須聲明基類(未向前聲明)。

當由另一個類,參數或返回值聲明對象成員時,必須聲明(未向前聲明)對象成員。注意:by-reference或by-pointer沒有這個約束。

更正... STL類的前向聲明 - 根據ISO 14882 - 未定義的行爲。 http://www.gotw.ca/gotw/034.htm