简体   繁体   English

从Java导入到C ++包括

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

I've been struggling with understanding how C++ classes include other classes. 我一直在努力理解C ++类如何包含其他类。 I'm guessing this is easier to understand without any preconceived notions. 我猜这很容易理解,没有任何先入为主的观念。

Assume my two classes are Library and Book. 假设我的两个课程是图书馆和书籍。 I have a .h and .cpp file for each. 我有一个.h和.cpp文件。 My "main.cpp" runs a simple console app to use them. 我的“main.cpp”运行一个简单的控制台应用程序来使用它们。 Here is a simple example: 这是一个简单的例子:

//Library.h

#ifndef LIBRARY_H_
#define LIBRARY_H_
#endif

class Library
{

public:
 Library();
 ~ Library();

private:
 Book *database;
};

This throws an error about how "Book does not name a type". 这会引发关于“Book没有命名类型”的错误。 In Java I would import some package like org.me.Domain.Book. 在Java中,我会导入一些像org.me.Domain.Book这样的包。 Can someone please explain how this works in C++? 有人可以解释一下这在C ++中是如何工作的吗?

In C++ source files are conceptually completely separate from class definitions. 在C ++中,源文件在概念上完全独立于类定义。

#include and header files work at a basic text level. #include和头文件在基本文本级别工作。 #include "myfile" simply includes the contents of the file myfile at the point at which the include directive is placed. #include "myfile"只包含放置include指令的文件myfile的内容。

Only after this process has happened is the resulting block of text interpreted as C++ code. 只有在此过程发生后,生成的文本块才会被解释为C ++代码。 There is no language requirement that a class called Book has to be defined in a file called Book.h . 没有语言要求必须在名为Book.h的文件中定义名为Book的类。 Although it is highly recommended that you do follow such a convention, it's essential to remember that it's not a given when debugging missing declaration or definition issues. 虽然强烈建议您遵循这样的约定,但必须记住,在调试缺少的声明或定义问题时,它不是给定的。

When parsing your Library.h file the compiler must have seen a declaration for the identifier Book at the point at which it is used in the defintion of the class Library . 当解析您的Library.h文件的编译器必须看到的标识符的声明Book的点,它在类的把定义中使用Library

As you are only declaring a member variable of type "pointer to Book ", you only need a declaration and not a full definition to be available so if Book is a class then the simplest 'fix' is to add a forward declaration for it before the definition of Library . 由于您只声明一个“指向Book指针”类型的成员变量,因此您只需要一个声明而不是完整定义,因此如果Book是一个类,那么最简单的“修复”是在之前为它添加前向声明Library的定义。

eg 例如

class Book;

class Library
{
    // definition as before
};

Include Guards 包括警卫

It looks like you may have some include guard errors. 看起来你可能有一些包括警卫错误。 Because you can only define classes once per translation units the definitions inside header files are usually protected with include guards. 因为每个转换单元只能定义一次类,所以头文件中的定义通常用include guard保护。 These ensure that if the same header is included multiple times via different include files that the definitions it provides aren't seen more than once. 这些确保如果通过不同的包含文件多次包含相同的头,则它提供的定义不会被多次看到。 Include guards should be arranged something like this. 包括警卫应该安排这样的事情。 Looking at your Library.h , it may be that your include guards are not terminated correctly. 查看您的Library.h ,可能是您的包含警卫没有正确终止。

myclass.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

Can someone please explain how this works in C++? 有人可以解释一下这在C ++中是如何工作的吗?

Hi, 嗨,

First, compiling in C++ is done in three steps: precompilation, compilation and linking. 首先,用C ++编译分三步完成:预编译,编译和链接。 The precompilation is: 预编译是:

  1. looking for "#include" directives 寻找“#include”指令
  2. expanding macros 扩展宏
  3. processing conditional compilation 处理条件编译

For 1., when you include a file, the code in the file is "pasted" into the compiled file from the first file matching the provided name found in the include path. 对于1.,当您包含文件时,文件中的代码将从第一个与包含路径中找到的提供名称匹配的文件“粘贴”到编译文件中。 The include path is specified to the compiler as input parameters (for gcc that is done with -I, so you can have gcc file.cpp -I. -I/usr/include and so on). include路径被指定给编译器作为输入参数(对于使用-I完成的gcc,所以你可以使用gcc file.cpp -I。-I / usr / include等)。

This "pasting of the code" can create problems, as one file can be (and usually is) included multiple times, in different files in your project. 这种“粘贴代码”可能会产生问题,因为一个文件可以(通常是)多次包含在项目的不同文件中。 This means that after the preprocessor has done it's job, you might encounter multiple definitions for the same symbols. 这意味着在预处理器完成它的工作后,您可能会遇到相同符号的多个定义。 To avoid compiler errors due to this, you use an "include guard" construct that looks like this: 为避免由此引起的编译器错误,您使用“include guard”结构,如下所示:

#ifndef SOME_UNIQUE_SYMBOL
#define SOME_UNIQUE_SYMBOL

// all the code in your file goes here

# endif // SOME_UNIQUE_SYMBOL

In this way, the first time the code is added (on the expansion of the #include by the precompilation process) it will be parsed (as SOME_UNIQUE_SYMBOL is undefined). 这样,第一次添加代码时(在预编译过程扩展#include时),它将被解析(因为SOME_UNIQUE_SYMBOL未定义)。 The second time, the code is added but will not be parsed (as SOME_UNIQUE_SYMBOL should already have been defined the first time). 第二次,代码被添加但不会被解析(因为SOME_UNIQUE_SYMBOL应该已经第一次定义)。

Microsoft C++ compilers define a #pragma once dirrective that you can use as the first line in a header file. Microsoft C ++编译器定义了#pragma once oncedirrective,您可以将其用作头文件中的第一行。 This ensures that the precompiler will only include the file once (effectively replacing the #ifdef / #define / #endif combo). 这可以确保预编译器只包含一次文件(有效地替换#ifdef / #define / #endif组合)。

Concretely in your example, your #endif should be the last line in the file. 具体来说,在您的示例中,您的#endif应该是文件中的最后一行。

This "pasting of the code" is also the reason why you separate declarations from definitions in C++: you place all declarations in header files (traditionally named something.h) and the definitions in source files (traditionally named something.cpp) and you only include header files. 这种“粘贴代码”也是你将声明与C ++中的定义分开的原因:你将所有声明放在头文件(传统上名为something.h)和源文件中的定义(传统上名为something.cpp)中包含头文件。

Your header files should always be minimal. 您的头文件应始终最小。 That is, they should only include declarations and enough #include directives for everything in the header file to be recognized (function and class names, constants and defines and so on). 也就是说,它们应该只包含声明和足够的#include指令,以便识别头文件中的所有内容(函数和类名,常量和定义等)。

Your example should be: 你的例子应该是:

//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

In this example, the compiler will need to know what is the size of the Library class when compiling it. 在此示例中,编译器在编译时需要知道Library类的大小。 For this requirement, it needs to know how big each of the member variables of Library is. 对于此要求,它需要知道Library的每个成员变量有多大。

In your case you only have a pointer to a book, so the size will be four bytes (or eight or something else, depending on processor architecture). 在您的情况下,您只有一个指向书的指针,因此大小将是四个字节(或八个或其他东西,具体取决于处理器架构)。

You still need to tell the compiler that "Book is a class" and you have two ways to do that: either use the forward declaration, or include the header file that defines the Book class (replace the class Book; code with #include "Book.h" . 你仍然需要告诉编译器“Book是一个类”,你有两种方法可以做到这一点:使用前向声明,或者包含定义Book类的头文件(替换类Book;使用#include "Book.h"代码#include "Book.h"

The forward declaration tells the compiler "Treat any Book token in the source as a class. You will find out the definition of the class later". 前向声明告诉编译器“将源中的任何Book令牌视为一个类。稍后您将找到该类的定义”。

If Book will not be found on linking (ie compiled as a separate object file and linked together with Library) the compiler will raise a linker error. 如果在链接上找不到Book(即编译为单独的目标文件并与库链接在一起),编译器将引发链接器错误。

If you use the #include you can also use a Book instance instead of a Book pointer in the header declaration (as when including it, you ensure the size of a Book class can be computed by the compiler when parsing Library class. 如果使用#include也可以在头文件声明中使用Book实例而不是Book指针(当包含它时,确保编译器在解析Library类时可以计算Book类的大小。

If you use the forward declaration, you will still have to use the #include in the source file of the Library class (in the .cpp class) where you actually use methods from the Book class. 如果使用前向声明,则仍然必须在Library类的源文件中使用#include(在.cpp类中),其中实际使用Book类中的方法。

Java imports don't have much in common with C++ #include directive. Java导入与C ++ #include指令没有多少共同之处。 Java import is just a convenience - when you write Java导入只是一种方便 - 当你写

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

the Java compiler knows, that each time you write MyClass you mean my.clever.package.with.a.very.long.name.MyClass . Java编译器知道,每次编写MyClass都意味着my.clever.package.with.a.very.long.name.MyClass But if you omit import and write my.clever.package.with.a.very.long.name.MyClass everywhere, it will be fine. 但是如果省略import并在任何地方写my.clever.package.with.a.very.long.name.MyClass ,那my.clever.package.with.a.very.long.name.MyClass This is because Java compiler does two-run compilation - in first run it finds out what classes exist and what interface they have and on the second run it compiles the code knowing about all classes defined in project and also in all libraries added to project. 这是因为Java编译器执行两次运行编译 - 在第一次运行时它会找出存在哪些类以及它们具有什么接口,并且在第二次运行时它会编译代码,知道项目中定义的所有类以及添加到项目中的所有库。

This is not the case with C++. C ++不是这种情况。 C++ compilers do one-run compilation . C ++编译器执行一次运行编译 There are several translation units (usually *.cpp files) - in your case I guess it is Library.cpp and Book.cpp. 有几个翻译单元 (通常是* .cpp文件) - 在你的情况下我猜它是Library.cpp和Book.cpp。 Each translation unit is compiled independently from any other and only at the end, in stage of linking, linker tries to combine results of each compilation. 每个翻译单元都是独立编译的,并且只在最后,在链接阶段,链接器尝试组合每个编译的结果。

Each translation unit is looked up from top to bottom and for every symbol used, there must be its declaration textually before using it. 每个翻译单元从上到下查找,对于使用的每个符号, 使用之前必须以文本方式进行声明。 As usually many translation units (*.cpp files) use the same symbols (for example they refer to same classes), to provide that definitions are the same or each unit (which is required), those definitions are put in header files (usually *.h files). 由于通常许多翻译单元(* .cpp文件)使用相同的符号(例如它们引用相同的类),以提供定义相同或每个单元(这是必需的),这些定义放在头文件中(通常* .h文件)。 And instead of copying class definition in each unit, you just #include definition files. 而不是在每个单元中复制类定义,只需#include定义文件。 But behind the curtain #include means just 'put the whole content of file.h in place where I wrote #include "file.h"'. 但幕后#include意味着'将file.h的全部内容放在我写#include“file.h”'的地方。

So remember 所以记住

  1. Everything must be declared before you use it. 在使用之前必须声明所有内容。
  2. If you use something in several .cpp files, put its definition in .h file and include it before using it. 如果您在几个.cpp文件中使用某些内容,请将其定义放在.h文件中并在使用之前包含它。

One more thing - I sometimes write declaration and sometimes definition - there are different things but it would make my answer much longer to explain it. 还有一件事 - 我有时写声明,有时候定义 - 有不同的东西,但它会让我的答案解释得更久。 Just do a research or ask another question about it - you are a newbie and you need much more to be able to write in C++. 只是做一个研究或询问另一个问题 - 你是一个新手,你需要更多能够用C ++写。 Anyway I recommend you C++ FAQ Lite 无论如何,我推荐你C ++ FAQ Lite

#include "Book.h"可能有效

You want to include the header of the Book class in this class. 您希望在此类中包含Book类的标题。

So: 所以:

#include "Book.h"

To avoid getting then a redefinition error you might want to put (in all your header files) the "#endif" preprocessor directive on the end of your file. 为避免出现重新定义错误,您可能希望在文件末尾放置(在所有头文件中)“#endif”预处理程序指令。

Your new library.h would be 你的新library.h会

#include Book.h

#ifndef LIBRARY_H_
#define LIBRARY_H_

class Library
{

public:
 Library();
 ~ Library();

private:
 Book *database;
};
#endif

This is because of the inclusion tree, the compiler is getting to the same header multiple times. 这是因为包含树,编译器多次到达相同的头。 The ifndef / define thing is for indicating that this header file is already processesd (and all the objects in it are already defined) ifndef / define事件用于指示此头文件已被处理(并且其中的所有对象都已定义)

The #endif in your Library.h file should be at the very end of the file, not after the #define LIBRARY_H_ . Library.h文件中的#endif应位于文件的最后,而不是#define LIBRARY_H_ Those three preprocessor directives (the two I mentioned and #ifndef LIBRARY_H_ ) ensure that the compiler only includes that file once. 这三个预处理器指令(我提到的两个和#ifndef LIBRARY_H_ )确保编译器只包含该文件一次。 If the file were included multiple times, then your Library class would have two definitions, which is illegal. 如果文件被多次包含,那么您的Library类将有两个定义,这是非法的。 You mentioned that you got a "redefinition of 'class Book'" error, which makes me think that you might have misplaced the #endif in your Book.h file, as well. 你提到你有一个“重新定义'类Book'”错误,这让我觉得你可能错误地放置了Book.h文件中的#endif

This is a small example of how you might combine the headers and cpp files in order to use your Library class. 这是一个小例子,说明如何组合头文件和cpp文件以使用Library类。 As already pointed out the forward declaration of Book in Library is only useful if the class definition of Library only contains pointers of type Book. 正如已经指出的那样,只有当Library的类定义包含Book类型的指针时,Book in Library中的前向声明才有用。 If you would do both forward declaration of book and including Book.h in Library.h you will get a compiler error "redefinition of class 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();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM