The inline and virtual keywords can be used as specifiers in function declarations. This use of the virtual keyword differs from its use in the base-class specifier of a class definition.
The inline specifier instructs the compiler to replace function calls with the code of the function body. This substitution is “inline expansion” (sometimes called “inlining”). Inline expansion alleviates the function-call overhead at the potential cost of larger code size.
The inline keyword tells the compiler that inline expansion is preferred. However, the compiler can create a separate instance of the function (instantiate) and create standard calling linkages instead of inserting the code inline. Several cases where this can happen are:
Recursive functions
Functions that are referred to through a pointer elsewhere in the translation unit
Functions that are declared as inline and that are not class member functions have internal linkage unless otherwise specified.
Microsoft Specific
The __inline keyword is equivalent to inline.¨As with normal functions, the order of evaluation of the arguments to an inline function is not defined. In fact, it could be different than the order in which the arguments are evaluated when passed using normal function call protocol.
Microsoft Specific
Recursive functions can be substituted inline to a depth specified by the inline_depthpragma. After that depth, recursive function calls are treated as calls to an instance of the function. The inline_recursionpragma controls the inline expansion of a function currently under expansion. See Chapter 13, “Preprocessing,”for more information about these pragmas. See the Environment and Toolsmanual for information about the /Ob command-line option that controls inline recursion.¨Inline Class Member Functions
A function defined in the body of a class declaration is an inline function. Consider the following class declaration:
class Account
{
public:
Account(double initial_balance) { balance = initial_balance; }
double GetBalance();
double Deposit( double Amount );
double Withdraw( double Amount );
private:
double balance;
};
The Account constructor is an inline function. The member functions GetBalance, Deposit, and Withdraw are not specified as inline but can be implemented as inline functions using code such as the following:
inline double Account::GetBalance()
{
return balance;
}
inline double Account::Deposit( double Amount )
{
return ( balance += Amount );
}
inline double Account::Withdraw( double Amount )
{
return ( balance -= Amount );
}
Note :
In the class declaration, the functions were declared without the inline keyword. The inline keyword can be specified in the class declaration; the result is the same.
A given inline member function must be defined exactly the same way in every compilation unit. This constraint causes inline functions to behave exactly as if they were instantiated functions. Additionally, there must be exactly one definition of an inline function.
A class member function defaults to external linkage unless a definition for that function contains the inline specifier. The example above shows that these functions need not be explicitly declared with the inline specifier; using inline in the function definition causes it to be an inline function. However, it is illegal to redeclare a function as inline after a call to that function.
Because inline implies static, it is an error to define an inline class member function with the static storage-class specifier.
Inline Functions versus Macros
Although inline functions are similar to macros (because the function code is expanded at the point of the call at compile time), inline functions are parsed by the compiler whereas macros are expanded by the preprocessor. As a result, there are several important differences:
Inline functions follow all the protocols of type safety enforced on normal functions.
Inline functions are specified using the same syntax as any other function except that they include the inline keyword in the function declaration.
Expressions passed as arguments to inline functions are evaluated exactly once. In some cases, expressions passed as arguments to macros can be evaluated more than once. The following example shows a macro that converts lowercase letters to uppercase:
#include <stdio.h>
#include <conio.h>
#define toupper(a) ((a) >= 'a' && ((a) <= 'z') ? ((a)-('a'-'A')):(a))
int main()
{
char ch = toupper( getch() );
printf( “%c”, ch );
return 0;
}
The intent of the expression toupper( getch() ) is that a character should be read from the console device (stdin) and if necessary, converted to uppercase.
Because of the implementation, getch is executed once to determine if the character is greater than or equal to “a” and once to determine if it is less than or equal to “z.” If it is in that range, getch is executed yet again to convert the character to uppercase. This means the program waits for two or three characters when the programmer intended it to wait for only one.
Inline functions remedy this problem:
#include <stdio.h>
#include <conio.h>
inline char toupper( char a )
{
return ((a >= 'a' && a <= 'z') ? a-('a'-'A') : a );
}
int main()
{
char ch = toupper( getch() );
printf( “%c”, ch );
return 0;
}
Inline functions are best used for small functions such as those used to access private data members. The main purpose of these one- or two-line “accessor” functions is to return state information about objects; short functions are sensitive to the overhead of function calls. Longer functions spend proportionately less time in the calling/returning sequence and benefit less from inlining.
The Point class, introduced in “Function Call Results” in Chapter 4, on topic can be optimized as follows:
class Point
{
public:
// Define “accessor” functions as
// reference types.
unsigned& x();
unsigned& y();
private:
unsigned _x;
unsigned _y;
};
inline unsigned& Point::x()
{
return _x;
}
inline unsigned& Point::y()
{
return _y;
}
Assuming coordinate manipulation is a relatively common operation in a client of such a class, specifying the two accessor functions (x and y in the preceding example) as inline typically saves the overhead of these procedures:
Function calls (including parameter passing and placing the object's address on the stack)
Preservation of caller's stack frame
New stack-frame setup
Return-value communication
Old stack-frame restore
Return
The virtual keyword can be applied only to nonstatic class member functions. It signifies that binding of calls to the function is deferred until run-time. Virtual functions are covered in their own section, “Virtual Functions” in Chapter 9, on topic .