何時可使用前向聲明?

我正在尋找什麼時候容許在另外一個類的頭文件中對一個類進行前向聲明的定義: 函數

我是否能夠針對基類,做爲成員持有的類,經過引用傳遞給成員函數的類等進行此操做? spa


#1樓

Lakos區分類用法 指針

  1. 僅名稱中 (對於此聲明,前向聲明已足夠)和
  2. in-size (須要爲其定義類)。

我從未見過它的發音更簡潔:) code


#2樓

我將其做爲一個單獨的答案而不是僅僅做爲評論,是由於我不一樣意Luc Touraille的回答,這不是基於合法性,而是基於強大的軟件和錯誤解釋的危險。 對象

具體來講,我對您的界面用戶但願瞭解的隱含合同有疑問。 get

若是您要返回或接受引用類型,那麼您只是說它們能夠經過指針或引用傳遞,而這些指針或引用又只能經過前向聲明來知道。 編譯器

當您返回不完整的類型時, X f2(); 那麼您說您的呼叫者必須具備X的完整類型規範。他們須要它才能在呼叫站點建立LHS或臨時對象。 io

一樣,若是您接受不完整的類型,則調用者必須構造了做爲參數的對象。 即便該對象做爲另外一個不完整類型從函數中返回,調用站點也須要完整的聲明。 即: 編譯

class X;  // forward for two legal declarations 
X returnsX();
void XAcceptor(X);

XAcepptor( returnsX() );  // X declaration needs to be known here

我認爲有一個重要的原則,即標頭應提供足夠的信息以使用它,而沒必要依賴其餘標頭。 這意味着在使用標頭聲明的任何函數時,標頭應該可以包含在編譯單元中,而不會引發編譯器錯誤。 模板

除了

  1. 若是外部的依賴是指望的行爲。 與其使用條件編譯,不如使用有據可查的要求,讓它們提供本身的標頭聲明X。這是使用#ifdefs的替代方法,而且是引入模擬或其餘變體的有用方法。

  2. 重要的區別是某些模板技術,其中明確不要求您實例化它們,只是提到了某人不會對我說鬼話。


#3樓

到目前爲止,沒有一個答案描述什麼時候可使用類模板的前向聲明。 因此,就到這裏。

能夠將類模板轉發聲明爲:

template <typename> struct X;

按照接受的答案的結構,

這是您能夠作的和不能作的。

使用不完整的類型能夠作什麼:

  • 聲明一個成員是另外一個類模板中不完整類型的指針或引用:

    template <typename T> class Foo { X<T>* ptr; X<T>& ref; };
  • 聲明一個成員爲其不完整實例之一的指針或引用:

    class Foo { X<int>* ptr; X<int>& ref; };
  • 聲明接受/返回不完整類型的函數模板或成員函數模板:

    template <typename T> void f1(X<T>); template <typename T> X<T> f2();
  • 聲明接受或返回其不完整實例之一的函數或成員函數:

    void f1(X<int>); X<int> f2();
  • 定義接受/返回不完整類型的指針/引用的函數模板或成員函數模板(但不使用其成員):

    template <typename T> void f3(X<T>*, X<T>&) {} template <typename T> X<T>& f4(X<T>& in) { return in; } template <typename T> X<T>* f5(X<T>* in) { return in; }
  • 定義接受/返回對其不完整實例之一的指針/引用的函數或方法(但不使用其成員):

    void f3(X<int>*, X<int>&) {} X<int>& f4(X<int>& in) { return in; } X<int>* f5(X<int>* in) { return in; }
  • 將其用做另外一個模板類的基類

    template <typename T> class Foo : X<T> {} // OK as long as X is defined before // Foo is instantiated. Foo<int> a1; // Compiler error. template <typename T> struct X {}; Foo<int> a2; // OK since X is now defined.
  • 使用它來聲明另外一個類模板的成員:

    template <typename T> class Foo { X<T> m; // OK as long as X is defined before // Foo is instantiated. }; Foo<int> a1; // Compiler error. template <typename T> struct X {}; Foo<int> a2; // OK since X is now defined.
  • 使用此類型定義功能模板或方法

    template <typename T> void f1(X<T> x) {} // OK if X is defined before calling f1 template <typename T> X<T> f2(){return X<T>(); } // OK if X is defined before calling f2 void test1() { f1(X<int>()); // Compiler error f2<int>(); // Compiler error } template <typename T> struct X {}; void test2() { f1(X<int>()); // OK since X is defined now f2<int>(); // OK since X is defined now }

不完整類型不能作的事情:

  • 將其實例化之一用做基類

    class Foo : X<int> {} // compiler error!
  • 使用其實例化之一來聲明一個成員:

    class Foo { X<int> m; // compiler error! };
  • 使用其實例化之一定義函數或方法

    void f1(X<int> x) {} // compiler error! X<int> f2() {return X<int>(); } // compiler error!
  • 使用其實例化之一的方法或字段,實際上試圖取消引用具備不完整類型的變量

    class Foo { X<int>* m; void method() { m->someMethod(); // compiler error! int i = m->someField; // compiler error! } };
  • 建立類模板的顯式實例化

    template struct X<int>;

#4樓

我只想添加一個重要的事情,您可使用Luc Touraille的答案中未提到的轉發類來作。

使用不完整的類型能夠作什麼:

定義接受/返回不完整類型的指針/引用並將該指針/引用轉發給另外一個函數的函數或方法。

void  f6(X*)       {}
void  f7(X&)       {}
void  f8(X* x_ptr, X& x_ref) { f6(x_ptr); f7(x_ref); }

一個模塊能夠經過一個前向聲明的類的對象傳遞給另外一個模塊。


#5樓

只要不須要定義(例如指針和引用),就能夠避免使用前向聲明。 這就是爲何大多數狀況下您會在標頭中看到它們的緣由,而實現文件一般會提取相應定義的標頭。

相關文章
相關標籤/搜索