如果我在头文件中实现了一个类,会发生什么

What happens if I implement a class in the header file?

本文关键字:一个 什么 文件 实现 如果      更新时间:2023-10-16

可能重复:
C++中的内联函数

如果我在类的头文件中完全实现了一个类,编译器会怎么做?一个典型的例子如下:

class MyException
{    
public:
    explicit MyException(const char* file, int line) file(file), line(line) {};
    const char* getFile() const { return file };
    int getLine() const { return line };
private:
    const char* const file;
    const int line;
};

我的意图是这样使用类:throw MyException(__FILE__, __LINE__)

我将这个头文件包含在每个.cpp文件中。我想编译器会根据类的定义多次编译它,并将(相同的)机器代码包含到它生成的每个对象文件中。现在,链接器将做什么?我尝试了一个更简单的例子(没有那些烦人的const),它编译得很好。

如果我在头文件中实现了一个三屏长的C函数,而不是一个简单的类,会发生什么?最后一个问题是,我应该把我的例子分成.h和.cpp文件吗?

所有方法都将是内联方法。你可能会在整个编译上浪费一些最短的时间,但这没关系。据我所知,唯一可能发生的问题是如果你有一个静态的非成本成员变量。然后你必须为它分配一个存储位置(如果你愿意,可以放置一个定义和初始值),大概在.cpp中,否则你会得到关于多个定义的链接器错误。

我见过在CPP中只有main()函数的仅标题的项目,但这是大量模板化的。

C++17的更新:自C++17以来,您可以在头文件中将静态非常数成员声明为inline。这使得只使用头的库很容易实现,而不需要在内联函数中使用类似体操的静态变量。

类定义本身不会生成任何代码。它只是向类的用户展示它是如何布局的,这样他们就可以生成适当的代码来操作它

生成代码的是类的成员函数。当您在类定义中定义成员函数时,它会为该函数提供一个隐式inline声明。

函数调用可以通过以下两种方式之一进行编译和链接:

(1) 可以将末尾带有RETURN汇编指令的函数代码的单个副本放置在映像中,并且可以将CALL汇编指令(以及参数传递和返回值传递)放置在调用站点,以将控制权传递到此代码。

(2) 函数实现的整个副本可以替换调用站点上的整个函数调用。

声明为inline的函数建议编译器使用第二种方法。此外,inline声明允许在几个翻译单元中定义函数(因此可以将其放置在共享头文件中)。为了让编译器可以选择实现第二种方法,它需要在编译时复制函数实现。如果函数实现在外国翻译单元中,则此选项不可用。

还应该注意的是,现代编译器使用内联声明的函数来处理复杂的事情。参见:

http://gcc.gnu.org/onlinedocs/gcc/Inline.html

当在头文件中实现成员函数时,所有这些函数都隐式地成为inline

这意味着什么?它有什么含义

根据,C++03标准§7.1.3/4:

  • 它提示编译器,在调用点替换函数体比通常的函数调用机制更可取
  • 即使省略了内联替换,也要遵循内联的其他规则(尤其是w.r.t One Definition Rule)

因此,是的,每个翻译单元都有inline函数的定义。这可能会导致二进制文件的大小增加。

通常,如果需要,任何优秀的主流编译器都会在调用时替换函数体,因此仅将函数inline标记为#1并不是一个好主意,但如果您想让类的用户清楚地了解意图,则可以通过在头中定义函数或显式地将函数标记为inline来实现。

我应该将示例拆分为.h.cpp文件吗

是的,这是大多数项目使用的常见编译模型,其中将接口(.h)与实现(.cpp)分离。这些接口作为头文件与代码的用户共享,而实现是以二进制文件的形式提供的。在某种程度上,这为您的知识产权提供了保障
这被称为分离模型

使用模板的C++项目通常会使用包含模型,而不是通常C++项目的分离模型。

相关文章: