头文件基本上是预先编写的类吗?C++

Are header files basically prewritten classes? c++

本文关键字:C++ 基本上 文件      更新时间:2023-10-16
正如

标题中所述,是您使用 #include 的头文件,基本上是预先编写/预编码的类,其中填充了方法和变量,一旦将其包含在脚本中,就可以使用这些方法和变量。另外,如何才能使它达到可以简单地使用您为自己的利益而创建的头文件的地步?一个简单的解释就可以了。

是和不是...这取决于很多事情。

包含指令 - 对于标准C++ - 可以是两件事:

#inlclude "filename"

#include <header>

第一个字面意思是"保留包含在"文件名"中的文本并将其粘贴到此处。所以是的,该文件可以包含预先编写的类(但不一定必须)

第二个意思是"让这个源使用标准在其<header>部分中定义的设施。根据标准定义,这不一定意味着是一个文件,尽管我今天知道的所有实现实际上都是:header 是一个文件名和<>""只是简单地更改编译器搜索顺序。

此时

,您可以将"很长的 Cpp 源"切割成更小的独立块,然后得到一个只不过是一系列#includemain()的 cpp 文件。

但这还不是全部。

C++程序不一定由单个 cpp 文件制作(cpp 文件加上它直接或间接包含的所有内容称为"翻译单元")。

事实上,编译产生的不是程序,而是"链接器"组合在一起的"对象文件"(术语是"古老的操作系统术语",与面向对象编程无关)。

因为调用另一个函数的C++函数不需要 - 被翻译 - 知道另一个函数是如何翻译的,而只需要如何传递和获取它的参数和返回值,这使得你能够在另一个独立编译的源代码中编写另一个函数体,并让标头仅包含其声明(而不是定义)。

例如,您可以拥有

// fooclass.h
#ifndef FOOCLASS_H
#define FOOCLASS_H
class foo
{
public:
   void hello();
   void bye();
};
#endif // FOOCLASS_H

// out.h
#ifndef OUT_H
#define OUT_H 
#include <string>
void printout(const std::string& s);
#endif // OUT_H

//main.cpp
#include "fooclass.h"
int main()
{
   foo afoo;
   afoo.hello();
   afoo.bye();
}

// fooclass.cpp
#include "fooclass.h"
#include "out.h"
void foo::hello()
{ printout("hello"); }
void foo::bye()
{ printout("bye"); }

// out.cpp
#include "out.h"
#include <iostream>
void printout(const std::string& s)
{ std::cout << "- " << s << " -" << std::endl; }

整个程序可以编译(例如使用GCC)为:

g++ main.cpp fooclass.cpp out.cpp

或以单独的步骤作为

g++ -c main.cpp
g++ -c fooclass.cpp
g++ -c out.cpp
g++ main.o fooclass.o out.o

(如果您使用 MS 编译器,您很可能会这样做

cl /c main.cpp
cl /c fooclass.cpp
cl /c out.cpp
link main.obj fooclass.obj out.obj

只有最后一步组成程序(注意main.cpp永远不会知道printout函数!前三个仅对已更改的文件是必需的,因此在大型项目中可以加快编译并将任务分配给不同的团队。(有一些像make这样的功能,可以自动化这件事。

有一个例外,如今越来越重要:模板。由于它们不是作为"要翻译的代码"实现的,而是作为"要扩展的代码"实现的,因此只有在知道它们必须扩展的内容时才能翻译它们。

template<class T>
T max(const T& a, const T& b)
{ return (a<b)? b: a; }

(a<b)的含义取决于自己T的实际类型。而且由于只有当调用max时才知道,因此max可以扩展为max<int>max<double>max<string>仅在使用时。

出于这个原因,模板通常不在独立的翻译单元中处理,提供模板类或函数的库通过仅提供包含所有代码的标头集合来实现这一点,而没有要单独编译的源代码。

您用于基本上预先编写/预编码类的 #include 的头文件是否充满了方法和变量,一旦将其包含在脚本中就可以使用?

这是一般概念。 在C++中,并非所有内容都需要在类中,因此也可以有非成员函数、类型、预处理器定义、变量等。 此外,一些标头声明变量、类型和/或函数,但不定义它们 - 这意味着您可以有限地使用它们,并且通常必须在链接或运行时呈现匹配的实现对象。

另外,如何才能使它达到可以简单地使用您为自己的利益而创建的头文件的地步?

它没有什么特别的魔力(尽管上面有一些链接问题可以了解你是否将实现放在外线),但首先只需在文件中抛出一些代码:

#pragma once
...

。然后从另一个文件中#include它。 如果包含文件位于目录"X"中,则可以在目录"X"中包含标头,例如 #include "the_header.h" - 使用双引号的头文件名 - 即使未将包含目录指定为编译器作为命令行参数。

如果标头中有已声明但未定义的内容,则需要一个与定义匹配的 .cc/.cpp 文件,您应该将其编译为对象并使用库与代码链接。 例如:

mylib.h
    #pragma one
    int fn();  // declared but not defined
mylib.cc
    #include "mylib.h"
    int fn() { return 42; }   // matching definition
cc -c mylib      // compile mylib.o
app.cc
    #include "mylib.h"
    int main()
    {
        return fn();
    }
cc -o app mylib.o

c++ 中头文件的常见用途是定义方法或类,而无需实现它们。它定义一个接口,或者告诉程序的其他部分如何与它们交互。它让执行包含操作的文件知道他们需要了解的有关这些类/方法的信息,而无需告诉他们不需要的所有信息。在方法的情况下,你知道有一个方法doSomethingCool,并且你知道如何与它交互。只要你知道你通常可以 #include 定义的任何头文件doSomethingCool,然后链接器在编译时发挥它的魔力,找到你调用doSomethingCool的位置并"连接它",以便与实际的实现代码交谈,你可以用doSomethingCool.cpp写。

适用于函数、类和变量。