7

可能重复:
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 文件吗?

4

3 回答 3

5

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

我见过只有头文件的项目,它只有main()CPP 中的功能,但它是大量模板化的。

于 2012-09-04T09:19:02.513 回答
4

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

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

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

(1) 可以在镜像中放置一个末尾带有 RETURN 汇编指令的功能代码的单个副本,并且可以在调用处放置一条 CALL 汇编指令(连同参数传递和返回值传递)以转移控制权到这个代码。

或者

(2) 函数实现的完整副本可以替换调用处的整个函数调用。

声明的函数inline是对编译器的建议,以第二种方式进行。此外,inline声明允许在多个翻译单元中定义函数(因此可以将其放置在共享头文件中)。为了让编译器可以选择实现第二种方法,它需要在编译时获得函数实现的副本。如果函数实现在外部翻译单元中,则此选项不可用。

还应该注意的是,现代编译器使用内联声明的函数做复杂的事情。看:

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

于 2012-09-04T09:18:21.277 回答
4

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

这是什么意思,它有什么影响?

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

  • 它提示编译器在调用点替换函数体优于通常的函数调用机制。
  • 即使省略了内联替换,也会遵循内联的其他规则(尤其是一个定义规则)。

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

通常,如果需要,任何好的主流编译器都会在调用点替换函数体,因此inline仅将函数标记为 #1 并不是一个好主意,但如果你想让你的类的用户清楚你的意图,那么你可以这样做在标头中定义函数或将函数显式标记为inline.

我应该把我的例子分成.h.cpp文件吗?

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

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

于 2012-09-04T09:57:16.393 回答