2010-02-19 44 views
4

我一直在努力理解C++類如何包含其他類。我猜這樣做比較容易理解,沒有任何先入爲主的觀念。從Java進口到C++包括

假設我的兩個類是Library和Book。我有每個.h和.cpp文件。我的「main.cpp」運行一個簡單的控制檯應用程序來使用它們。這裏有一個簡單的例子:

//Library.h 

#ifndef LIBRARY_H_ 
#define LIBRARY_H_ 
#endif 

class Library 
{ 

public: 
Library(); 
~ Library(); 

private: 
Book *database; 
}; 

這引發了一個有關「書不命名類型」的錯誤。在Java中我會導入一些包如org.me.Domain.Book。有人可以解釋這是如何在C++中的作品?

+0

我正在尋找C++如何使用包含在這個實例中的解釋,而不僅僅是對錯誤的修復。 – Stephano 2010-02-19 08:09:52

+1

你的編譯後衛是不適當的:在所有聲明之後,你應該在文件的最後有一個'#endif'。 – 2010-02-19 08:17:55

+0

我推薦這篇文章:http://www.gamedev.net/reference/programming/features/orgfiles/ – fredoverflow 2010-02-19 10:16:14

回答

7

在C++源文件是從概念類的定義完全分離。

#include和頭文件工作在基本的文本級別。 #include "myfile"只是在放置i​​nclude指令的位置包含文件myfile的內容。

只有在發生此過程後,纔會將結果塊的文本解釋爲C++代碼。沒有語言要求,稱爲Book的類必須在名爲Book.h的文件中定義。儘管強烈建議您遵循這樣的約定,但要記住,在調試缺少的聲明或定義問題時,它不是給定的。

解析Library.h文件時,編譯器必須在類Library的定義中使用標識符Book時看到聲明。

當你只聲明類型的成員變量「指針Book」,你只需要一個聲明,而不是一個完整的定義可用,所以如果Book是一類,那麼最簡單的「修復」是添加一個正向在Library的定義之前聲明它。

例如

class Book; 

class Library 
{ 
    // definition as before 
}; 

include防範

看起來你可能有一些包括後衛失誤。由於每個翻譯單元只能定義一次類,所以頭文件中的定義通常使用包含守護進行保護。這些確保如果通過不同的包含文件多次包含相同的頭文件,則它提供的定義不會被多次看到。包括警衛應該安排這樣的事情。看着你的Library.h,它可能是你的包括守衛沒有正確終止。

myclass.h:

#ifndef MYCLASS_H 
#define MYCLASS_H 

class MyClass 
{ 
}; 

// The #ifndef is terminated after all defintions in this header file 
#endif //MYCLASS_H 
+0

這很有道理。謝謝你清理#include。現在,我嘗試了這個,並收到一個錯誤「invalid use of incomplete type'struct Book'」,我在Library中爲Book.h添加了一個include。 cpp,並且錯誤消失了。我不知道爲什麼或者如果這是做正確事情的正確方法:)。 – Stephano 2010-02-19 08:17:01

+0

(我猜這是不正確的,因爲我不想在我的Library.cpp文件中包含Book.h文件...我認爲) – Stephano 2010-02-19 08:18:14

+0

聽起來好像你需要'Book'類的完整定義對於您在'Library.cpp'中執行的操作。如果是這種情況,並且該類在'Book.h'中定義,那麼你應該從'Library.cpp'中'#include「Book.h」'。你爲什麼認爲你不想這樣做? – 2010-02-19 08:21:08

1

#include "Book.h"可能工作

+0

該代碼,當放置在Library.h,將在本書產生一個錯誤「‘類書’的重新定義」。 H。 – Stephano 2010-02-19 08:09:19

+0

@Stephano:這就是爲什麼book.h還應該有一個編譯後衛(即類似於'#ifndef BOOK_H #define BOOK_H ...'和'#endif'這個文件的最後部分(library.h沒有)。 – 2010-02-19 08:17:11

0

您要包括Book類的這個類的頭。

所以:

#include "Book.h" 

要避免遇到那麼你可能希望把(在所有的頭文件)的「#ENDIF」預處理指令在你的文件的末尾重新定義錯誤。

您的新library.h將

#include Book.h 

#ifndef LIBRARY_H_ 
#define LIBRARY_H_ 

class Library 
{ 

public: 
Library(); 
~ Library(); 

private: 
Book *database; 
}; 
#endif 

這是因爲夾雜樹,編譯器是找相同的標題多次。 ifndef/define是用來表示這個頭文件已經被處理過了(並且其中的所有對象已經被定義)

0

你的Library.h文件中的#endif應該在文件的最後,而不是在#define LIBRARY_H_。這三個預處理器指令(我提到的兩個和#ifndef LIBRARY_H_)確保編譯器只包含該文件一次。如果文件被多次包含,那麼你的Library類將有兩個定義,這是非法的。你提到你有一個「重新定義'Book'錯誤,這讓我覺得你可能錯過了Book.h文件中的#endif

0

這是一個小例子,說明如何組合頭文件和cpp文件,以便使用 您的Library類。正如已經指出的那樣,Book中庫的前向聲明只有在庫的類定義只包含Book類型的指針時纔有用。如果你要在Library.h中同時做前向聲明書和包含Book.h,你將得到一個編譯器錯誤「重新定義類Book」。

// File Book.h 
#ifndef BOOK_H 
#define BOOK_H  
class Book 
{ 
};  
#endif 

// File Library.h 
#ifndef LIBRARY_H 
#define LIBRARY_H 

// forward declararion of Book 
class Book; 

class Library 
{ 
    public: 
    Library();  
    ~ Library(); 

    void CreateNewBook(); 

    private: 
    Book* m_database; 
}; 

// File Library.cpp 
#include "Library.h" 
#include "Book.h" // now neede in order to create instances of class Book 

Library::Library() : m_database(NULL) {} 
Library::~ Library() 
{ 
    delete m_database; 
} 


void Library::CreateNewBook() 
{ 
    m_database = new Book(); 
} 

// main.cpp 
#include "Library.h" 

int main() 
{ 
    Library myLib; 

    myLib.CreateNewBook(); 
} 
2

Java導入與C++ #include指令沒有很多共同之處。 Java導入僅僅是一個方便的 - 當你寫

import my.clever.package.with.a.very.long.name.MyClass; 

Java編譯器知道,每一個你寫MyClass時候你的意思my.clever.package.with.a.very.long.name.MyClass。但是,如果您省略了import並在各處都寫入my.clever.package.with.a.very.long.name.MyClass,那就沒問題。這是因爲Java編譯器雙運行編譯 - 在第一次運行時,它發現哪些類存在以及它們具有哪個接口,並且在第二次運行時編譯代碼,以瞭解項目中定義的所有類以及添加到所有庫中的代碼項目。

這不是C++的情況。 C++編譯器做一次編譯。有幾個翻譯單元(通常是* .cpp文件) - 在你的情況下,我想它是Library.cpp和Book.cpp。每個翻譯單元都是獨立編譯的,只有在最後,在鏈接階段,鏈接器纔會嘗試合併每個編譯的結果。

從上到下對每個翻譯單元進行查找,對於每個使用的符號,在之前必須使用作爲其聲明。由於通常很多翻譯單元(* .cpp文件)使用相同的符號(例如它們指的是相同的類),爲了提供定義是相同的或每個單元(這是必需的),這些定義放在頭文件中(通常* .h文件)。而不是在每個單元中複製類定義,只需#include定義文件。但是在#幕後面#include意味着'將file.h的全部內容放在我編寫#include「file.h」的地方。

所以請記住

  1. 一切都必須聲明你使用它之前。
  2. 如果您在幾個.cpp文件中使用某些內容,請將其定義放在.h文件中並在使用它之前包含它。

還有一件事 - 我有時會寫聲明和有時定義 - 有不同的東西,但這會讓我的回答更長時間來解釋它。只要做一個研究或問另一個問題 - 你是一個新手,你需要更多的能夠寫在C + +。無論如何,我建議你C++ FAQ Lite

3

有人可以請解釋這是如何工作在C + +?預編譯,編譯和鏈接:

嗨,

首先,在C++編譯分三個步驟完成。該預編譯:

  1. 尋找「的#include」指令
  2. 擴大宏
  3. 處理條件編譯

爲1,當你包含一個文件,該文件中的代碼將「」粘貼到與包含路徑中提供的名稱匹配的第一個文件的編譯文件中。包含路徑作爲輸入參數指定給編譯器(對於使用-I完成的gcc,因此可以使用gcc file.cpp -I。-I/usr/include等)。

This 「代碼粘貼」會產生問題,因爲一個文件可以(通常是)多次包含在項目的不同文件中。這意味着在預處理器完成它的工作後,您可能會遇到同一符號的多個定義。爲了避免由於這個編譯器錯誤,您可以使用「包括衛士」構造,看起來像這樣:

#ifndef SOME_UNIQUE_SYMBOL 
#define SOME_UNIQUE_SYMBOL 

// all the code in your file goes here 

# endif // SOME_UNIQUE_SYMBOL 

通過這種方式,第一時間代碼是由預編譯增加(在#包括擴大進程)它將被解析(因爲SOME_UNIQUE_SYMBOL是未定義的)。第二次添加代碼但不會被解析(因爲SOME_UNIQUE_SYMBOL應該是第一次定義)。

Microsoft C++編譯器定義了一個#pragma once dirrective,您可以將它用作頭文件中的第一行。這可以確保預編譯器只包含一次文件(有效替換組合文件#ifdef/#define/#endif)。

具體在你的例子中,你的#endif應該是文件的最後一行。

「的代碼粘貼」也是爲什麼從用C定義你單獨聲明++:您將所有的聲明在頭文件中(傳統命名something.h),並在源文件中定義(傳統命名的東西.cpp),並且只包含頭文件。

您的頭文件應該始終最小。也就是說,他們應該只包含聲明和足夠的#include指令來識別頭文件中的所有內容(函數和類名,常量和定義等)。

你的榜樣應該是:

//Library.h 

#ifndef LIBRARY_H_ 
#define LIBRARY_H_ 

class Book; // forward declaration, see below 

class Library 
{ 

public: 
Library(); 
~ Library(); 

private: 
Book *database; 
}; 

#endif // moved as the last line of the file 

在這個例子中,編譯器將需要知道編譯時它是什麼Library類的大小。對於這個需求,它需要知道庫的每個成員變量有多大。

你的情況,你只有一個指向一本書,所以規模將是四個字節(或八個或別的東西,這取決於處理器架構)。

您仍然需要告訴編譯器「Book是一個類」,並且您有兩種方法可以這樣做:使用forward聲明或包含定義Book類的頭文件(替換Book類;代碼與#include "Book.h"

向前聲明告訴編譯器「源爲一類治療任何令牌。以後你會發現這個類的定義」。

如果圖書不會被上找到鏈接(即編譯爲單獨的目標文件並與庫鏈接在一起)編譯器將引發鏈接器錯誤。

如果您使用#include,您還可以在頭聲明中使用Book實例而不是Book指針(因爲包含它時,確保編譯器在解析Library類時可以計算Book類的大小。

如果使用前置聲明,你仍然必須使用的#include庫類的源文件(在的.cpp類),你實際使用的方法從Book類。