2008年10月6日星期一

[TIC++] C9. Inline Functions

代码阅读 <Thinking in C++>
Chapter 9. Inline Functions

一、什么是Inline

// c09: macro_side_effects.cpp
#include <fstream>
using namespace std;

#define BAND(x) (((x) > 5 && (x) < 10) ? (x) : 0)

int main()
{
ofstream out("macro.out");
// assure(out, "macro.out");
for (int i = 4; i < 11; i++) {
int a = i;
out << "a = " << a << endl << "\t";
out << "BAND(++a)" << BAND(++a) << endl;
out << "\t a = " << a << endl;
}

}
/* macro.out:
a = 4
BAND(++a)0
a = 5
a = 5
BAND(++a)8
a = 8
a = 6
BAND(++a)9
a = 9
a = 7
BAND(++a)10
a = 10
a = 8
BAND(++a)0
a = 10
a = 9
BAND(++a)0
a = 11
a = 10
BAND(++a)0
a = 12
*/

第一个例子,介绍了宏(macro)带来的负面效应。BAND(x)定义了一个三元运算符,C/C++中
会对选择语句中的与运算和或运算进行优化,(x)>5为false之后,将不会考虑(x)<10,而自
增运算符结合macro会产生预料之外的效果。

为了消除奇异,避免此类情况发生,我们引入了内联方法——

// c09: inline.cpp
#include <iostream>
#include <string>
using namespace std;

class Point {
int i, j, k;
public:
Point(): i(0), j(0), k(0) {}
Point(int ii, int jj, int kk)
: i(ii), j(jj), k(kk) {}
void print(const string &msg = "") const
{
if (msg.size() != 0)
cout << msg << endl;
cout << "i = " << i << ","
<< "j = " << j << ","
<< "k = " << k << endl;
}
};

int main()
{
Point p, q(1,2,3);
p.print("value of p");
q.print("value of q");
}

这里和以前并没有什么异常,这证明人的习惯是很可怕的……我们要说的是,以前在类中定义
的所有函数,都是内联函数(inline functions)。内联函数也只是存在于编译期内,编译的
时候,编译器会打开内联函数的代码,直接迭代到调用处。他起到了宏的作用,而且避免了
负面效用。

考虑一下print(const string&)方法,如果没有内联,那么print代码本身就要将this指针
放入堆栈,并且使用一次汇编语句CALL,大多数机器中,这段代码的长度会比内联展开的代
码量要大,而且花费时间也会更长。

二、使用内联
这个例子包含了三个代码段stash4.h,stash4.cpp,stash4test.cpp。

stash4.h

// c09: stash4.h
// inline functions

#ifndef __STASH4_H
#define __STASH4_H

class stash {
int size;
int quantity;
int next;
unsigned char *storage;
void inflate(int increase);
public:
stash(int sz): size(sz), quantity(0), next(0), storage(0) {}
stash(int sz, int init_quantity): size(sz), quantity(0), next(0), storage(0)
{
inflate(init_quantity);
}

~stash()
{
if (storage != 0)
delete []storage;
}

void *fetch(int index) const
{
if (index >= next)
return 0;
return &(storage[index * size]);
}

// declarations of non-inline functions
int add(void *element);
int count() const { return next; }
};


#endif // __STASH4_H

stash4.cpp

// c09: stash4.cpp
#include "stash4.h"
#include <iostream>
#include <cassert>
using namespace std;

const int increment = 100;

int stash::add(void *element)
{
if (next >= quantity)
inflate(increment);
int start_bytes = next * size;
unsigned char *e = (unsigned char*)element;

for (int i = 0; i < size; i++)
storage[start_bytes + i] = e[i];
next ++;
return(next -1);
}


void stash::inflate(int increase)
{
assert(increase >= 0);
if (increase == 0)
return;
int new_quantity = quantity + increase;
int new_bytes = new_quantity * size;
int old_bytes = quantity * size;
unsigned char *b = new unsigned char[new_bytes];

for (int i = 0; i < old_bytes; i++)
b[i] = storage[i];
delete [](storage);
storage = b;
quantity = new_quantity;
}
///:~

stash4test.cpp

// c09: stash4test.cpp
#include "stash4.h"
#include <fstream>
#include <iostream>
#include <string>
using namespace std;

int main()
{
stash int_stash(sizeof(int));

for (int i = 0; i < 100; i++)
int_stash.add(&i);
for (int j = 0; j < int_stash.count(); j++)
cout << "int_stash.fetch(" << j << ") = "
<< *(int*)int_stash.fetch(j)
<< endl;

const int bufsize = 80;
stash string_stash(sizeof(char) * bufsize, 100);
ifstream in("stash4test.cpp");
string line;
while(getline(in, line))
string_stash.add((char*)line.c_str());

int k = 0;
char *cp;
while ((cp = (char*)string_stash.fetch(k++)) != 0)
cout << "string_stash.fetch(" << k << ") = "
<< cp << endl;
}
/* result: 结果中途省略
int_stash.fetch(0) = 0
int_stash.fetch(1) = 1
int_stash.fetch(2) = 2
int_stash.fetch(3) = 3
...
int_stash.fetch(98) = 98
int_stash.fetch(99) = 99
string_stash.fetch(1) = // c09: stash4test.cpp
string_stash.fetch(2) = #include "stash4.h"
string_stash.fetch(3) = #include <fstream>
string_stash.fetch(4) = #include <iostream>
string_stash.fetch(5) = #include <string>
string_stash.fetch(6) = using namespace std;
string_stash.fetch(7) =
string_stash.fetch(8) = int main()
string_stash.fetch(9) = {
...
string_stash.fetch(31) = }
*/

stash中的简单方法在这里用到了inline,他们更加的高效和简洁。但也要留意两个最大的
方法在外部被定义,这样他们就不是inline方法,因为这样作不会带来任何效益。

三、向前引用特性

// c09: evaluation_order.cpp
// inline evaluation order

class forward {
int i;
public:
forward(): i(0) {}
// call undeclared function
int f() const { return g() + 1; }
int g() const { return 1; }
};

int main()
{
forward fwd;
fwd.f();
}

注意到,f()在g()声明或者定义之前调用了g(),这在inline函数世界里变得可能了。
_

没有评论:

发表评论