2.17 What is an inline function?
When we define a function, it is usually
compiled into a self-contained unit of code. For example, a
function
int
foo
(
int
a
,
int
b
)
{
return
a
+
b
-
1;
}
would compile into a block of code equivalent to
stack[1] = stack[3] + stack[2] - 1;
jump to address in stack[0]
where the “stack” is the runtime stack a.k.a. the activation
stack used to track function calls at the system
level, stack[0] is the top value on the stack, stack[1] the value
just under that one, and so on. A function call like
would be compiled into a code sequence along the lines of
push y onto the runtime stack;
evaluate z+1;
push the result onto the runtime stack
push (space for the return value) onto the runtime stack
save all CPU registers
push address RET onto the runtime stack
jump to start of foo's body
RET: x = stack[1]
pop runtime stack 4 times
restore all CPU registers
As you can see, there’s a fair amount of overhead involved in
passing parameters and return address information to a function
when making a call. The amount of time spent on this overhead is
really all that large. If the function body contains several
statements of any kind of loop, then the overhead is probably a
negligable fraction of the total time spent on the call.
class
Foo
{
private
:
int
bar
;
public
:
int
getBar
();
void
setBar
(
int
);
};
int
Foo
::
getBar
()
{
return
bar
;}
void
Foo
::
setBar
(
int
b
)
{
bar
=
b
;}
But many ADTs have member functions that are only one or two lines
long, and often trivial lines at that. For these functions, the
overhead associated with each call may exceed the time required to
do the function body itself. Furthermore, because these functions
are often the primary means of accessing the ADT’s contents,
sometimes these functions get called thousands of times or more
inside the application’s loops.
For these kinds of trivial functions, C++ offers
the option of declaring them as inline.
class Foo {
private:
int bar;
public:
int getBar () {return bar;}
void setBar (int);
};
inline
void Foo::setBar (int b) {bar = b;}
An inline function can be written one of two
ways. First, it can be written inside the
class declaration. Second, we can place
the reserved word inline in
front of the function definition written in its usual place outside
the class declaration. When we make a call to an inline function,
the compiler simply replaces the call by a compiled copy of the
function body (with some appropriate renaming of variables to avoid
conflicts). So, if we have
inline
int
foo
(
int
a
,
int
b
)
{
return
a
+
b
-
1;
}
and we later make a call
This would be compiled into a code sequence along the lines of
evaluate z+1, storing result in tempB
evaluate y + tempB - 1, storing result in x
Most of the overhead of making a function call hs been eliminated.
Inline functions can reduce the run time of a
program by removing unnecessary function calls, but, used unwisely,
may also cause the size of the program to explode. Consequently,
they should be used only by frequently-called functions with bodies
that take only 1 or 2 lines of code. For larger functions, the
times savings would be negligable (as a fraction of the total time)
while the memory penalty is more severe, and for infrequently used
functions, who cares?
Inlining is only a recommendation from the
programmer to the compiler. The compiler may ignore an inline
declaration and continue treating it as a conventional function if
it prefers. In particular, note that inlining of functions with
recursive calls is impossible, as is inlining of most virtual
function calls. Many compilers will refuse to inline any function
whose body contains a loop. Others may have their own peculiar
limitations.
