为什么类成员函数内联?

时间:2012-03-16 08:45:36

标签: c++ class function compiler-construction inline

我认为我之前已经问过我的问题,我确实已经阅读了它们,但仍然有点困惑,因此要求说清楚。

The C++ standard says all member functions defined inside class definition are inline

我也听说编译器可以忽略函数的内联。在上述情况下是否会成立,或者如果在类定义中定义它将始终内联?

此外,这个设计背后的原因是什么,使所有函数在类定义中内联定义?内联与源文件和头文件有什么关系?

更新:所以如果不要内联,那么应该总是在课外定义他们的函数,对吗?

JohnB更新2:在类定义中声明的两个函数永远不能互相调用,因为它们必须包含另一个函数的整个主体。在这种情况下会发生什么?(已由Emilio Garavaglia回答)

6 个答案:

答案 0 :(得分:37)

出现混乱,因为内联有两个影响:

  1. 它告诉编译器函数代码可以展开调用函数,而不是有效被调用
  2. 它告诉编译器可以重复函数定义。
  3. 第1点是“过时的”,因为编译器实际上可以按照自己喜欢的方式来优化代码。它总是“内联”机器代码,如果它可以并且发现方便,如果它不能,它将永远不会那样做。

    第2点是该术语的实际含义:如果你在标题中define(指定正文)一个函数,由于标题可以包含在更多的源中,你必须告诉编译器通知关于定义重复的链接器,以便它们可以合并。

    现在,根据语言规范,自由函数(未在类体中定义)默认情况下未定义为内联,因此在标题中定义类似

    的内容
    void myfunc()
    {}
    

    如果标头包含在更多源中,然后在同一输出中链接,则链接器将报告多重定义错误,因此需要将其定义为

    inline void fn()
    {}
    

    对于类成员,默认情况相反:如果您只声明它们,则不会内联它们。如果您定义它们,它们将是内联的。

    所以标题看起来应该是

    //header file
    
    class myclass
    {
    public:
        void fn1()
        {} //defined into the class, so inlined by default
    
        void fn2();
    };
    
    inline void myclass::fn2()
    {} //defined outside the class, so explicit inline is needed
    

    如果myclass::fn2()定义属于正确的来源,则必须丢失inline关键字。

答案 1 :(得分:10)

inline关键字具有函数2含义:

  1. 代码替换:在调用inline函数的地方,不要为它生成函数调用,只需放置函数的内容即可 在它的调用地方(这类似于宏 更换,但输入安全)
  2. 一个定义规则:不要为inline函数生成多个定义,只生成所有共同的单个定义(例外:static函数)
  3. 第1个术语(“代码替换”)只是编译器的请求。这可以忽略,因为编译器最好判断是放文本还是函数调用。 (例如,virtual函数或递归函数不能内联。)

    第二个术语(“一个定义规则”)保证由任何符合标准的编译器发生。这将为所有翻译单元仅生成1个定义。这种设施有时可以简化编码器的工作,因为较小的功能可能不希望将其定义放在.cpp文件中(例如,吸气剂,设定者)。
    此外,对于仅{header}构造的template函数,此效果是必需的。因此,template函数默认为inline

    示例:

    class A {
    public:
      void setMember (int i) { m_i = i; }
    };
    

    在这个例子中,大多数编译器都可以满足这两个术语

    class A {
      inline virtual ~A () = 0;
    };
    A::~A() {}
    

    这里编译器只能满足第二个要求。

答案 2 :(得分:8)

使方法函数内联的唯一原因是如果在标题中定义它。

如果在标题中定义方法函数,并且未放入内联关键字,并且在多个标题或源文件中包含标题,则可以获得该方法的多个定义。

9.3 / 2成员函数[class.mfct]中的c ++ 11标准告诉:

  

成员函数可以在其类定义中定义(8.4),在这种情况下,它是内联成员函数(7.1.2)......

答案 3 :(得分:2)

如果inline关键字指定,编译器可以忽略内联。如果方法实现存在于类定义中,那就是一个不同的东西,并且不能被忽略。 (它可以,但这会使编译器不符合)

设计背后的原因 - 我需要一种机制,你可以实际强制编译器实际内联你的函数,因为inline关键字并没有强制要求它。但一般来说,内联方法定义仅在getter和setter方法或一些简单的2-liners的情况下完成。和模板,但这是一个不同的问题。

内联与头文件和源文件有关,因为函数的定义必须对编译器可见,因此它知道如何实际内联调用。内联在实现文件中定义的函数比在头文件中定义的函数更难。

编辑:在旁注中,op正在审视的段落为7.1.2.3

  

在类定义中定义的函数是内联函数[...]。

EDIT2:

显然,内联函数和内联替换之间存在一些差异。第一个是函数的属性,它不仅包括内联替换,第二个意味着函数体实际粘贴在调用它的位置。

因此,该函数可以内联,但不能粘贴其体,而不是被调用。

答案 4 :(得分:2)

当定义在类中时,它被视为声明为inline,因为假定类定义存在于从多个翻译单元使用的头文件中,所以任何非这里的内联定义将违反一个定义规则。

编译器一如既往地可以自由地内联它所想的内容,只要注意显式或隐式内联的函数不会导致链接器错误。如何通过语言规范保持开放 - 内联函数的功能,但也可以降低符号可见性或将符号重命名为特定于翻译单元的名称(就好像该函数位于匿名名称空间中一样) ),或(如他们大多数人所做的那样)与链接器通信可能存在该函数的多个副本,并且它应该丢弃除了其中一个之外的所有副本。

因此,简而言之,它与明确声明为inline的函数没有任何不同。

答案 5 :(得分:0)

你要做的两件事是不同的方面,不要混淆。

  

1)C ++标准说在类定义中定义的所有成员函数都是内联的

     

2)我也听说编译器可以忽略函数的内联

1)是在类声明本身内定义成员函数的时候。即:在头文件中。为此,您不必提供任何关键字(即:inline

2)您可以通过明确使用inline关键字将函数指定为内联。这实际上是对编译器的请求。根据一些优化规则,编译器可能会也可能不会使函数内联。