定义内部成员函数与类定义之外的成员函数之间是否存在差异?

时间:2014-04-28 17:30:02

标签: c++ coding-style

考虑以下四个成员函数声明和定义:

// ==== file: x.h
#ifndef X_H
#define X_H
class X {
 public:
  int a(int i) { return 2 * i; }
  inline int b(int i) { return 2 * i; }
  int c(int i);
  int d(int i);
};

inline int X::c(int i) { return 2 * i; }
int X::d(int i) { return 2 * i; }
#endif

为了完整性,这里是实例化X并调用方法的.cpp文件......

// ==== file: x.cpp
#include "x.h"
#include <stdio.h>

int main() {
  X x;
  printf("a(3) = %d\n", x.a(3));
  printf("b(3) = %d\n", x.b(3));
  printf("c(3) = %d\n", x.c(3));
  printf("d(3) = %d\n", x.d(3));

  return 0;
}

我的问题:四种方法之间有什么显着差异吗?我从this post中的注释中了解到,编译器可以自动内联在类定义中定义的方法。

更新

许多答案都假设我在询问内联与否之间的区别。我不是。正如我在原始帖子中提到的,我理解在头文件中定义方法会给编译器许可证内联方法。

我(现在)也明白方法d是有风险的,因为它没有内联,如果有多个翻译单元,它将被多重定义。

我的问题仍然存在:这四种方法中是否存在明显的差异? (如上所述,我知道方法d是不同的)。但是 - 同样重要的是 - 有没有风格或惯用的考虑因素会让开发人员选择其他人而不是其他人?

5 个答案:

答案 0 :(得分:5)

由于这个答案不断提升,我觉得有必要改进它。但我正在添加的大部分内容已在其他答案和评论中说明,而这些作者应该得到赞誉。

关于将函数体放在类定义中还是在它下面(但仍然在头文件中)之间是否存在差异的问题,有三种不同的情况需要考虑:< / p>

1)该函数不是模板,并且未声明为内联函数。在这种情况下,它必须在类定义或单独的cpp中定义,否则只要您尝试将h包含在多个编译单元中,就会出现链接器错误。

2)该函数是模板,但未声明为内联。在这种情况下,将主体放在类定义中会向编译器提示可以内联函数(但最终决定仍由其自行决定)。

3)该函数声明为内联。在这种情况下,没有语义差异,但有时可能需要将函数体放在底部以适应依赖循环。

原始答案,提供了很好的信息,但没有解决实际问题:

您已经注意到了内联差异。此外,在标题中定义成员函数意味着您的实现对每个人都可见。更重要的是,这意味着包含标题的每个人都需要包含使您的实现工作所需的所有内容。

答案 1 :(得分:2)

如果您打算无论如何内联它,那么如果您希望能够在一个屏幕中看到所有成员,或者您具有循环依赖性,则将其移出课程,如下所述。如果您不想内联它,那么您必须将它移出类并进入实现文件。

在循环引用彼此的类的情况下,可能无法在类中定义函数以便内联它们。在这种情况下,要实现相同的效果,您需要将这些函数移出类。

不编译:

struct B;
struct A {
    int i;
    void foo(const B &b) {
        i = b.i;
    }
};

struct B {
    int i;
    void foo(const A &a) {
        i = a.i;
    }
};

编译,并达到同样的效果:

struct B;
struct A {
    int i;
    inline void foo(const B &b);
};

struct B {
    int i;
    inline void foo(const A &a);
};

inline void A::foo(const B &b) {
    i = b.i;
}
inline void B::foo(const A &a) {
    i = a.i;
}

答案 2 :(得分:1)

哎呀,刚刚意识到你在头文件中有定义。如果包含文件包含在多个位置,则会产生问题。

如果在CPP文件中定义了这些功能,则没有区别。

答案 3 :(得分:0)

实现函数内联的唯一时间是函数非常明显无关紧要和/或它具有性能影响。

在所有其他时间,最好将它们放在.cc文件中,并保持其实现不会暴露给类的用户。

正如user3521733所指出的,当存在循环依赖时,不可能在头文件中实现某些功能。在这里,您被迫将实现放在.cc文件中。

<强>更新

就编译器和运行时而言,如果在外部定义函数时使用inline,则在定义函数体内部或外部函数之间没有区别。全班同学。

答案 4 :(得分:0)

X::aX::bX::c都是inline d。 X::d不是。除了它们都是不同的功能之外,这是这些功能之间唯一真正的差异。标头中定义X::c这一事实无关紧要。相关的是定义标记为inline

为了理解差异是什么,了解inline是什么和不是什么很重要。 inlinenot a performance tweak。它不是要让你的代码更快,而不是内联代码。

它是ODR。标记为inline的函数在使用它的每个翻译单元中具有完全相同的定义。

当您在两个或更多CPP文件中尝试#include上述文件并在这些翻译单元中调用X::d时,可以使用此功能。链接器会抱怨X::d被定义不止一次 - 您违反了ODR。对此的修复是标记函数inline或将定义移动到它自己的翻译单元。 (例如,到CPP文件)