从Java导入到C ++包括

时间:2010-02-19 07:59:02

标签: java c++ class

我一直在努力理解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 ++中是如何工作的吗?

7 个答案:

答案 0 :(得分:7)

在C ++中,源文件在概念上与类定义完全分开。

#include和头文件在基本文本级别工作。 #include "myfile"只是在放置i​​nclude指令的位置包含文件myfile的内容。

只有在此过程发生后,生成的文本块才会被解释为C ++代码。没有语言要求必须在名为Book的文件中定义名为Book.h的类。虽然强烈建议您遵循这样的约定,但必须记住,在调试缺少的声明或定义问题时,它不是给定的。

在解析Library.h文件时,编译器必须在标识Book的定义中看到标识符Library的声明。

由于您只声明一个类型为“Book指针”的成员变量,因此您只需要声明而不是完整定义,因此如果Book是一个类,那么最简单的'修复'是在Library的定义之前为它添加前向声明。

e.g。

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

答案 1 :(得分:3)

  

有人可以解释一下这在C ++中是如何工作的吗?

您好,

首先,在C ++中进行编译分三步完成:预编译,编译和链接。预编译是:

  1. 寻找“#include”指令
  2. 扩展宏
  3. 处理条件编译
  4. 对于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 dirrective,您可以将其用作头文件中的第一行。这可确保预编译器仅包含文件一次(有效替换#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"

    前向声明告诉编译器“将源中的任何 Book 标记视为一个类。稍后您将找到该类的定义”。

    如果在链接上找不到Book(即编译为单独的目标文件并与Library链接在一起),编译器将引发链接器错误。

    如果你使用#include,你也可以在头文件声明中使用Book实例而不是Book指针(当包含它时,你确保编译时解析器可以计算Book类的大小)图书馆班。

    如果使用前向声明,您仍然必须在Library类的源文件中使用#include(在.cpp类中),其中实际使用Book类中的方法。

答案 2 :(得分: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文件中并在使用之前将其包含在内。
  3. 还有一件事 - 我有时会写声明,有时也会定义 - 有不同的东西,但它会让我的答案更长一段时间来解释它。只是做一个研究或询问另一个问题 - 你是一个新手,你需要更多能够用C ++写。无论如何,我建议你C++ FAQ Lite

答案 3 :(得分:1)

#include "Book.h"可能会有效

答案 4 :(得分: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事件用于指示此头文件已被处理(并且其中的所有对象都已定义)

答案 5 :(得分:0)

Library.h文件中的#endif应位于文件的最末端,而不是#define LIBRARY_H_之后。这三个预处理器指令(我提到的两个和#ifndef LIBRARY_H_)确保编译器只包含该文件一次。如果文件被多次包含,那么您的Library类将有两个定义,这是非法的。你提到你有一个“重新定义'类Book'”错误,这让我觉得你可能错误地放了Book.h文件中的#endif

答案 6 :(得分:0)

这是一个小例子,说明如何组合头文件和cpp文件以便使用 你的图书馆课。正如已经指出的那样,只有当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();
}