Is there a difference between defining member functions inside vs outside the class definition?

StackOverflow https://stackoverflow.com/questions/23347506

  •  11-07-2023
  •  | 
  •  

Domanda

Consider the following four member function declarations and definitions:

// ==== 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

For completeness, here's the .cpp file that instantiates an X and calls the methods...

// ==== 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;
}

My question: are there any salient differences among the four methods? I understand from a comment in this post that the compiler may automatically inline methods that are defined in the class definition.

update

Many answers assume that I'm asking about the difference between inlining and not. I'm not. As I mentioned in the original post, I understand that defining a method in the header file gives the compiler license to inline the method.

I also (now) understand that method d is is risky as written: since it is not inlined, it will be multiply defined if there are multiple translation units.

My question remains: are there any salient differences among the four methods? (As noted, I know that method d is different). But -- just as important -- are there stylistic or idiomatic considerations that would make a developer choose one over the others?

È stato utile?

Soluzione

Since this answer keeps getting upvotes, I feel obligated to improve it. But much of what I'm adding has already been stated in other answers and comments, and those authors deserve the credit.

On the subject of whether there's a difference between placing a function body inside the class definition or just below it (but still in the header file), there are 3 different cases to think about:

1) The function is not a template and is not declared to be inline. In this case it must be defined in the class definition or a separate cpp or you will get a linker error as soon as you try to include the h in more than one compilation unit.

2) The function is a template, but is not declared inline. In this case, putting the body within the class definition provides a hint to the compiler that the function can be inlined (but the final decision is still at its own discretion).

3) The function is declared to be inline. In this case there is no semantic difference, but it may sometimes be necessary to place the function body at the bottom in order to accommodate dependency cycles.

Original answer, which provides good info but does not address the actual question:

You've already noted the inline difference. In addition, defining member functions in the header means your implementation is visible to everyone. More importantly, it means everyone who includes your header also needs to include everything needed to make your implementations work.

Altri suggerimenti

If you are going to inline it regardless, then you'd move it out of the class if you want to be able to see all your members in one screen, or you have a cyclic dependency as mentioned below. If you don't want to inline it, then you have to move it out of the class and into an implementation file.

In the cases of classes that cyclically refer to each other, it may be impossible to define the functions in the classes so as to inline them. In that case, to achieve the same effect, you need to move the functions out of the classes.

Doesn't compile:

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;
    }
};

Does compile, and achieves the same effect:

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;
}

Oops, just realised you had the definitions in the header file. That creates problems if the include file is included in more than one place.

If the functions are defined in a CPP file then there is no difference.

The only time it makes sense to implement a function inline is when the function is very clearly trivial and/or it has performance implications.

For all other times, it's best to put them in a .cc file and keep its implementation not exposed to the user of the class.

As pointed out by user3521733, it is impossible to implement some functions in the header file when there are cyclic dependencies. Here you are forced to put the implementations in a .cc file.

Update

As far as the compiler, and the runtime, is concerned, there is no difference that I can think of between defining the function inside the body of the class or outside if you use inline when defining it outside the body of the class.

X::a, X::b and X::c are all inlined. X::d is not. That's the only real differnce between these functions, aside from the fact that they are all different functions. The fact that X::c is defined in the header is irrelevant. What is relevant there is that the definition is marked inline.

In order to understand what the differences are, it's important to understand what inline is and is not. inline is not a performance tweak. It's not about making your code faster, and it's not about blowing the code out inline.

What it is about is the ODR. A function marked inline will have the exact same definition in each translation unit where it is used.

This comes in to play when you try to #include your file above in two or more CPP files and call X::d in those translation units. The linker will complain that X::d is defined more than once -- you've violated the ODR. The fix to this is to either mark the function inline or move the definition to it's own translation unit. (eg, to a CPP file)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top