



(9 ratings)
Suppose that you wish to write a function in C to compute the maximum
of two numbers. One way would be to say:
int max(int a, int b)
{
return (a > b ? a : b);
}
But calling a frequently-used function can be a bit slow, and so you
instead use a macro:
#define max(a, b) ((a) > (b) ? (a) : (b))
The extra parentheses are required to handle cases like:
max(a = b, c = d)
This approach can work pretty well. But it is error-prone due to the
extra parentheses and also because of side effects like:
max(a++, b++)
An alternative in C++ is to use inline functions:
inline int max(int a, int b)
{
return (a > b ? a : b);
}
Such a function is written just like a regular C or C++ function. But
it IS a function and not simply a macro; macros don't really obey the
rules of C++ and therefore can introduce problems. Note also that one
could use C++ templates to write this function, with the argument
types generalized to any numerical type.
If an inline function is a member function of a C++ class, there are a
couple of ways to write it:
class A {
public:
void f() { /* stuff */ } // "inline" not needed
};
or:
class A {
public:
inline void f();
};
inline void A::f()
{
/* stuff */
}
The second style is often a bit clearer.
The "inline" keyword is merely a hint to the compiler or development
environment. Not every function can be inlined. Some typical reasons
why inlining is sometimes not done include:
- the function calls itself, that is, is recursive
- the function contains loops such as for(;;) or while()
- the function size is too large
Most of the advantage of inline functions comes from avoiding the
overhead of calling an actual function. Such overhead includes saving
registers, setting up stack frames, and so on. But with large
functions the overhead becomes less important.
Inline functions present a problem for debuggers and profilers,
because the function is expanded at the point of call and loses its
identity. A compiler will typically have some option available to
disable inlining.
Inlining tends to blow up the size of code, because the function is
expanded at each point of call. The one exception to this rule would
be a very small inline function, such as one used to access a private
data member:
class A {
int x;
public:
int getx() {return x;}
};
which is likely to be both faster and smaller than its non-inline
counterpart.
A simple rule of thumb when doing development is not to use inline
functions initially. After development is mostly complete, you can
profile the program to see where the bottlenecks are and then change
functions to inlines as appropriate.
Here's a complete program that uses inline functions as part of an
implementation of bit maps. Bit maps are useful in storing true/false
values efficiently. Note that in a couple of places we could use the
new bool fundamental type in place of ints. Also note that this
implementation assumes that chars are 8 bits in width; there's no
fundamental reason they have to be (in Java(tm) the Unicode character set
is used and chars are 16 bits).
This example runs about 50% faster with inlines enabled.
#include <assert.h>
#include <stdlib.h>
#include <string.h>
//#define inline
class Bitmap {
typedef unsigned long UL; // type of specified bit num
UL len; // number of bits
unsigned char* p; // pointer to the bits
UL size(); // figure out bitmap size
public:
Bitmap(UL); // constructor
~Bitmap(); // destructor
void set(UL); // set a bit
void clear(UL); // clear a bit
int test(UL); // test a bit
void clearall(); // clear all bits
};
// figure out bitmap size
inline Bitmap::UL Bitmap::size()
{
return (len - 1) / 8 + 1;
}
// constructor
inline Bitmap::Bitmap(UL n)
{
assert(n > 0);
len = n;
p = new unsigned char[size()];
assert(p);
clearall();
}
// destructor
inline Bitmap::~Bitmap()
{
delete [] p;
}
// set a bit
inline void Bitmap::set(UL bn)
{
assert(bn < len);
p[bn / 8] |= (1 << (bn % 8));
}
// clear a bit
inline void Bitmap::clear(UL bn)
{
assert(bn < len);
p[bn / 8] &= ~(1 << (bn % 8));
}
// test a bit, return non-zero if set
inline int Bitmap::test(UL bn)
{
assert(bn < len);
return p[bn / 8] & (1 << (bn % 8));
}
// clear all bits
inline void Bitmap::clearall()
{
memset(p, 0, size());
}
#ifdef DRIVER
main()
{
const unsigned long N = 123456L;
int i;
long j;
int k;
int r;
for (i = 1; i <= 10; i++) {
Bitmap bm(N);
// set all bits then test
for (j = 0; j < N; j++)
bm.set(j);
for (j = 0; j < N; j++)
assert(bm.test(j));
// clear all bits then test
for (j = 0; j < N; j++)
bm.clear(j);
for (j = 0; j < N; j++)
assert(!bm.test(j));
// run clearall() then test
bm.clearall();
for (j = 0; j < N; j++)
assert(!bm.test(j));
// set and clear random bits
k = 1000;
while (k-- > 0) {
r = rand() & 0xffff;
bm.set(r);
assert(bm.test(r));
bm.clear(r);
assert(!bm.test(r));
}
}
return 0;
}
#endif
20 Random Tutorials from the same category :
Bjarne Stroustrup's C++ Style and Technique FAQ 3
Objects in C++
Type Names In C++
Hidden Constructor/Destructor Costs
Function Prototypes
Data Structures
Handling a Common strcmp() Case
Constructors and Integrity Checking
Functions (I)
Brief History of C++
Branching Statements (if, else, switch) in C++
Compiling and Running in C ++
"Hello, World" Program in C++
Function Overloading
What is a Function in C++ ?
A Coding Convention for C++ Code
Operator New/Delete
A Manual of C Style
Variable Types and Declaring Variables in C++
What is a Variable in C++ ?














