簡體   English   中英

從Java導入到C ++包括

[英]Going from Java imports to C++ includes

我一直在努力理解C ++類如何包含其他類。 我猜這很容易理解,沒有任何先入為主的觀念。

假設我的兩個課程是圖書館和書籍。 我有一個.h和.cpp文件。 我的“main.cpp”運行一個簡單的控制台應用程序來使用它們。 這是一個簡單的例子:

//Library.h

#ifndef LIBRARY_H_
#define LIBRARY_H_
#endif

class Library
{

public:
 Library();
 ~ Library();

private:
 Book *database;
};

這會引發關於“Book沒有命名類型”的錯誤。 在Java中,我會導入一些像org.me.Domain.Book這樣的包。 有人可以解釋一下這在C ++中是如何工作的嗎?

在C ++中,源文件在概念上完全獨立於類定義。

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

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

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

由於您只聲明一個“指向Book指針”類型的成員變量,因此您只需要一個聲明而不是完整定義,因此如果Book是一個類,那么最簡單的“修復”是在之前為它添加前向聲明Library的定義。

例如

class Book;

class Library
{
    // definition as before
};

包括警衛

看起來你可能有一些包括警衛錯誤。 因為每個轉換單元只能定義一次類,所以頭文件中的定義通常用include guard保護。 這些確保如果通過不同的包含文件多次包含相同的頭,則它提供的定義不會被多次看到。 包括警衛應該安排這樣的事情。 查看您的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

有人可以解釋一下這在C ++中是如何工作的嗎?

嗨,

首先,用C ++編譯分三步完成:預編譯,編譯和鏈接。 預編譯是:

  1. 尋找“#include”指令
  2. 擴展宏
  3. 處理條件編譯

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

這種“粘貼代碼”可能會產生問題,因為一個文件可以(通常是)多次包含在項目的不同文件中。 這意味着在預處理器完成它的工作后,您可能會遇到相同符號的多個定義。 為避免由此引起的編譯器錯誤,您使用“include guard”結構,如下所示:

#ifndef SOME_UNIQUE_SYMBOL
#define SOME_UNIQUE_SYMBOL

// all the code in your file goes here

# endif // SOME_UNIQUE_SYMBOL

這樣,第一次添加代碼時(在預編譯過程擴展#include時),它將被解析(因為SOME_UNIQUE_SYMBOL未定義)。 第二次,代碼被添加但不會被解析(因為SOME_UNIQUE_SYMBOL應該已經第一次定義)。

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

具體來說,在您的示例中,您的#endif應該是文件中的最后一行。

這種“粘貼代碼”也是你將聲明與C ++中的定義分開的原因:你將所有聲明放在頭文件(傳統上名為something.h)和源文件中的定義(傳統上名為something.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類的大小。 對於此要求,它需要知道Library的每個成員變量有多大。

在您的情況下,您只有一個指向書的指針,因此大小將是四個字節(或八個或其他東西,具體取決於處理器架構)。

你仍然需要告訴編譯器“Book是一個類”,你有兩種方法可以做到這一點:使用前向聲明,或者包含定義Book類的頭文件(替換類Book;使用#include "Book.h"代碼#include "Book.h"

前向聲明告訴編譯器“將源中的任何Book令牌視為一個類。稍后您將找到該類的定義”。

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

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

如果使用前向聲明,則仍然必須在Library類的源文件中使用#include(在.cpp類中),其中實際使用Book類中的方法。

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 ,那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

#include "Book.h"可能有效

您希望在此類中包含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事件用於指示此頭文件已被處理(並且其中的所有對象都已定義)

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

這是一個小例子,說明如何組合頭文件和cpp文件以使用Library類。 正如已經指出的那樣,只有當Library的類定義包含Book類型的指針時,Book in Library中的前向聲明才有用。 如果您同時執行書籍的預告並在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();
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM