2010-09-03 113 views
14

考慮以下兩個方案(只是編輯,以完成整個問題,使其更清晰)向前聲明VS包括

案例1:(犯規編譯爲正確下面提到)

//B.h 
#ifndef B_H 
#define B_H 
#include "B.h" 

class A; 

class B { 
     A obj; 
     public: 
     void printA_thruB(); 

     }; 
#endif 

//B.cpp 
#include "B.h" 
#include <iostream> 

void B::printA_thruB(){ 
     obj.printA(); 
     } 


//A.h; 
#ifndef A_H 
#define A_H 

#include "A.h" 

class A { 
     int a; 
     public: 
     A(); 
     void printA(); 

     }; 
#endif 

//A.cpp       
#include "A.h"      
#include <iostream>    

A::A(){       
     a=10;      
     }       

void A::printA()     
{         
std::cout<<"A:"<<a<<std::endl;  
} 


//main.cpp 
#include "B.h" 
    #include<iostream> 
using namespace std; 

int main() 
{ 
B obj; 
obj.printA_thruB(); 
} 

案例2: (唯一的修改......工作沒有編圖誤差)

//B.h 

#include "A.h" //Add this line 
//class A;  //comment out this line 

讓我們假設兩個A.cpp和B.cpp得到遵守在一起。以上兩種情況有什麼不同嗎?有沒有理由更喜歡一種方法而不是另一種?

編輯: 那麼,我該如何讓情景1工作。

+1

所以你已經編譯A.cpp和B.cpp在一起,但是你對A.h和B.h做了什麼? – 2010-09-03 03:20:30

+0

@丹尼斯讓你的問題更清晰 – Sii 2010-09-03 04:06:27

回答

13

編譯B.cpp時,情況1將產生「不完整類型」錯誤。因爲B類包含A類對象,所以A類的定義(特別是尺寸)需要在定義B類之前完成。

或者,您可以選擇使some_variable成爲指針或引用A類,在這種情況下,您的前向聲明在Bh中就足夠了你仍然需要B.cpp中A的完整定義(假設你實際使用了A成員函數/數據)。

+2

澄清。如果您嘗試調用A上的任何方法,則情況1只會產生編譯錯誤。在這種情況下,它應該給出錯誤,因爲您將隱式地調用構造函數。但是在另一種情況下,如果B的某個方法只是接收到一個指向A對象的指針,然後將它傳遞給B的責任範圍之外的某個其他方法/函數,那麼編譯器會很好地知道A的定義。 – Hitesh 2010-09-03 03:43:54

+0

你是對的畫了我得到一個編譯錯誤。那我該怎麼過來呢? 我認爲前向聲明可以用來代替標頭包含。 – Sii 2010-09-03 04:15:56

+0

@Hitesh:不會。因爲編譯器不知道sizeof(A),因此無法知道如何佈置B對象,因此,Case 1將始終生成編譯錯誤。 – 2010-09-03 05:40:56

4

如果您有相互引用的類,則需要使用前向聲明。

//A.h 

class B; 

class A { 
    B* someVar; 
} 

//B.h 
#include <A.h> 

class B { 
    A* someVar; 
} 

但是,如果在佈置的情況下這樣做沒有任何好處。

+0

感謝您的信息。 如果B在我的情況下訪問A的某個變量,該怎麼辦。 – Sii 2010-09-03 04:09:41

+0

在你的情況下,你真的想要在B.h.中使用A.h的顯式包含。只有兩個實例看到了使用的前向引用:第一個是我上面提到的關於循環引用的第二個實例,第二個是供應商提供的頭文件只有一些類只定義爲前向引用。他們已經清理了頭文件以保護他們的專有信息。最終的結果是,與他們的API一起工作是一場噩夢 - 我不推薦它。 – Hitesh 2010-09-03 04:20:05

+0

我正在嘗試做類似的事情..從接口(.h)刪除標題,並向前聲明我嘗試使用的類。 – Sii 2010-09-03 04:51:26

2

如果您打算將some_variable作爲指針進行描述,那麼經常推薦的做法是儘可能使用forward聲明以避免包含開銷和更長的編譯時間。

我都是最佳實踐,但我真的很喜歡使用具有良好代碼導航功能的IDE,並且在那裏導致問題,至少在Netbeans中。每當我嘗試導航到一個類型聲明時,我總是在前進而不是包含實際聲明的.h文件。爲了便於導航,我願意接受一些額外的編譯時間。也許這只是Netbeans的一個問題:)

..哦是的..如果你看看你的問題的權利相關的問題,你會發現許多前瞻性聲明的額外信息。

+0

這是我試圖避免..不需要的標題包含。 – Sii 2010-09-03 04:17:46

3

像編譯器一樣思考。爲了在B中創建一個A,編譯器必須知道如何構建A,唯一的方法就是擁有完整的定義。前向聲明告訴編譯器,類A存在而不描述它的外觀;這對於定義指針或引用是足夠的。當需要使用該指針或引用時,將需要完整的類定義。

+0

因此,如果我在B.cpp文件中包含頭文件「A.h」,並且只是在B.h中聲明類A,它應該工作嗎? – Sii 2010-09-03 04:48:02

+0

@MrProg,不,它不會工作 - Bh定義瞭如何創建一個B,但是如果不知道如何創建一個A,因爲B包含一個A,您無法創建B.您可以將指針或引用存儲到A而不知道如何創建一個A,這就是爲什麼前向引用可能有用,而不是你的情況。 – 2010-09-03 05:03:19

+0

如果我將'A obj'改爲'A * obj',並且按照上面提到的那樣將A類中的'int a'變爲公共​​的,我可以編譯..但是我確實遇到了分段錯誤。 所以我認爲我對前向聲明的理解需要重新考慮。 – Sii 2010-09-03 05:19:37

8

前向聲明不是頭文件包含的替代品。

正如其名稱本身所暗示的,正向聲明只是一個Declaration而不是一個定義

所以,你會聲明編譯器它是一個類,我只是在這裏聲明它,並且會爲你提供何時使用它的定義。所以,通常你在的頭文件中和#include中的.cpp文件中,你會在中使用這個forward聲明類的成員。

通過這樣做,你做什麼,不管你是包括頭文件中,有也只是爲階級,而不是全部內容#included聲明...

不過話說回來,當編譯器需要類的定義,它應該是#included ..

所以,你的情況A obj;需要class A定義,因此你應該#include ..

我自己問過類似的問題here和另一similar question這也一個很好的答案...

希望它可以幫助..

+0

雖然包括標題,我們做同樣的事情..它只是聲明..definition進入.cpp源文件。所以如果我改變我的'A obj'到'A * obj'這是一個指針它應該是正確的。(但不是) – Sii 2010-09-03 05:24:23

+0

感謝您的幫助..我已經迷茫了自己一天我的想法。 – Sii 2010-09-03 05:33:53

0

對於情況1,編譯器會用「不完全型」告狀類B,因爲B類包含A類對象,你沒有告訴B類A的任何細節,所以編譯器水溼決定對象B.

的大小對於你的情況,你可以使用A& objA* obj代替A obj,因爲參考/指針的大小是常量(32位/ 64位CPU爲4/8)。