4

假设我有一个 X 类(Xh):

class X {
  private:
    unsigned unitsSold = 0;
    double revenue = 0.0;

  public:
    double avgPrice();
}

avgPrice()应该定义哪种方式?

选项 1(课堂):

Xh

class X {
  private:
    unsigned unitsSold = 0;
    double revenue = 0.0;

  public:
    double avgPrice() {
      return unitsSold ? revenue / unitsSold : 0;
    }
}

选项 2(与类在同一文件中,但在类定义之外):

Xh

class X {
  private:
    unsigned unitsSold = 0;
    double revenue = 0.0;

  public:
    double avgPrice();
}

inline double X::avgPrice() {
  return unitsSold ? revenue / unitsSold : 0;
}

选项 3(在单独的头文件中):

Xh:

class X {
  private:
    unsigned unitsSold = 0;
    double revenue = 0.0;

  public:
    double avgPrice();
}

X-inl.h:

#include "X.h"

inline double X::avgPrice() {
  return unitsSold ? revenue / unitsSold : 0;
}
4

6 回答 6

6

说明符的含义可能存在一些误解inline。是的,它确实向编译器提供了一个提示,即最好内联代码而不是进行调用,但编译器不会被迫遵守这个提示。说明符的主要用途inline是避免违反One Definition Rule

一旦你声明了一个函数inline,它需要在它使用的每个翻译单元中定义,并且每次的定义都必须完全相同。这与您的标题所暗示的相反 - 您定义函数的位置的选择决定了它是否需要标记inline

1) 和 2) 都可以。在第一种情况下,它是隐式inline的,而在第二种情况下,您明确地声明了它。无论您包含标题,定义都是相同的。

情况 3) 仅在您编译和链接X_impl.h为源文件时才有效。在这种情况下,将只有一个定义并且inline是多余的。但是,这样编译器就看不到其他翻译单元中的定义,这使得它不可能内联函数,不管它是否inline存在。

如果 的目的X_impl.h是减少标题的视觉大小,那么您应该反过来做,将其包含在X.h. inline必须留在那种情况下。

于 2013-08-13T07:17:38.737 回答
1

我会选择有利于可读性的每种方法,这取决于函数的大小:

  • 单行功能 --> 选项 1,
  • 小尺寸函数 --> 选项 2,
  • 中等规模的功能 --> 选项 3,
  • 大尺寸函数 --> 你确定要内联吗?

如果您有大量小型函数,请选择选项 3,切勿将选项 2 和 3 混合在一起。

此外,当您提出第三个选项时,您必须记住包含X-inl.h而不是X.h. 如果修改如下:

Xh:

#ifndef _CLASS_X_H_
#define _CLASS_X_H_
class X {
  private:
    unsigned unitsSold = 0;
    double revenue = 0.0;

  public:
    double avgPrice();
};
#include "X-inl.h"
#endif

X-inl.h:

inline double X::avgPrice() {
  return unitsSold ? revenue / unitsSold : 0;
}

然后你可以X.h像往常一样包含。

于 2013-08-13T07:08:49.737 回答
1

这三个选项都是正确的。

在它取决于这样的事情之后:

  • 如果你的函数很短(比如getter/setter),更常见的是直接在类定义中定义函数。

  • 如果你的函数更大,最好在另一个头文件中定义它,并在使用函数的源代码中只包含这个头文件。这只会加快您的编译速度。但很少有inline大功能。

但是不要忘记,并不是因为你使用了inline关键字,你的编译就会内联你的函数。由编译器决定是否在它使用的每个地方都使用该函数。

这在标准中明确说明:

7.1.2/2 函数说明符 [dcl.fct.spec]

带有inline说明符的函数声明声明了一个内联函数。inline 说明符向实现表明,在调用点对函数体进行内联替换优于通常的函数调用机制。在调用点执行此内联替换不需要实现;然而,即使省略了这个内联替换,7.1.2 中定义的内联函数的其他规则仍应得到遵守。

最后一件事 :

大多数编译器已经优化代码以在更方便时生成内联函数。此说明符仅指示此函数首选内联的编译器。


正如 jrok 所说,inline主要用于避免违反单一定义规则。在这里,我们还可以引用标准的一小部分:

(第 7.1.2/4 节)内联函数应在使用它的每个翻译单元中定义,并且在每种情况下都应具有完全相同的定义(3.2)。

3.2 一个定义规则[basic.def.odr]

任何翻译单元都不得包含一个以上的任何变量、函数、类类型、枚举类型或模板的定义。

于 2013-08-13T07:00:58.610 回答
0

这取决于您在哪里使用内联函数,以及您使用它的频率。如果内联函数的代码很短(就像大多数 getter/setter 一样)并且(可能)在许多地方使用,那么直接将其放入类定义中是最直接的方法。

如果您的内联函数是“大量的”并且仅由您的类的少数用户使用,则最好将其放入单独的标题中。在这些情况下加速编译,不需要内联,但需要您将该额外的头文件传达给您的库的用户。

于 2013-08-13T06:53:43.000 回答
0

inline我认为关键字和内联函数/方法可能会有些混淆。

关键字inline告诉编译器它应该将函数/方法代码复制到调用函数/方法的地方。例子:

/* [...] */
inline void sayHello() {
    std::cout << "Hello" << std::endl;
}

int main(int argc, char **argv) {
    sayHello();
    return 0;
}

将成为

/* [...] */
int main(int argc, char **argv) {
    std::cout << "Hello" << std::endl;
    return 0;
}

编译时。但是编译器不会强制内联函数/方法。

另一方面,内联方法是在您声明它的地方实现的。例子:

/* [...] */
class X {
private:
    unsigned int unitsSold;
    double revenue;

public:
    /* [...] */
    double avgPrice() {
        if (unitsSold == 0) {
            return 0.0;
        }
        return revenue/unitsSold;
    }
};
于 2013-08-13T06:59:15.713 回答
-1

只要您将inline建议添加到编译器,您提供的三个选项将(必须)导致相同的编译代码。

那总是考虑使用标准编译器执行标准编译任务......

于 2013-08-13T06:48:49.587 回答