《C++编程思想》第八章 内 联 函 数 (知识点+习题+解答)

一.相关知识点

任何在类中定义的函数自动地成为内联函数,但也可以使用inline关键字放在类外定义的函数前面使之成为内联函数。但为了使之有效,必须使函数体和声明结合在一起,否则,编译器将它作为普通函数对待。因此

inline int PlusOne(int x);

没有任何效果,仅仅只是声明函数(这不一定能够在稍后某个时候得到一个内联定义)。成功的方法如下:

inline int PlusOne(int x) { return ++x ;}

在头文件里,内联函数默认为内部连接——即它是 static, 并且只能在它被包含的编译单元看到。因而,只要它们不在相同的编译单元中声明,在内联函数和全局函数之间用同样的名字也不会在连接时产生冲突。

局限性

这儿有两种编译器不能处理内联的情况。在这些情况下,它就像对非内联函数一样,通过定义内联函数和为函数建立存贮空间,简单地将其转换为函数的普通形式。假如它必须在多编译单元里做这些(通常将产生一个多定义错误),连接器就会被告知忽略多重定义。

假如函数太复杂,编译器将不能执行内联。这取决于特定编译器,但大多数编译器这时都会放弃内联方式,因为这时内联将可能不为我们提供任何效率。一般地,任何种类的循环都被认为太复杂而不扩展为内联函数。循环在函数里可能比调用要花费更多的时间。假如函数仅有一条简单语句,编译器可能没有任何内联的麻烦,但假如有许多语句,调用函数的开销将比执行函数体的开销少多了。记住,每次调用一个大的内联函数,整个函数体就被插入在函数调用的地方,所以没有任何引人注目的执行上的改进就使代码膨胀。本书的一些例子可能超过了一定的合理内联尺寸。

假如我们要显式或隐含地取函数地址,编译器也不能执行内联。因为这时编译器必须为函数代码分配内存从而为我们产生一个函数的地址。但当地址不需要时,编译器仍可能内联代码。我们必须理解内联仅是编译器的一个建议,编译器不强迫内联任何代码。一个好的编译器将会内联小的、简单的函数,同时明智地忽略那些太复杂的内联。这将给我们想要的结果—具有宏效率的函数调用。

小结

能够隐藏类下面的实现是关键的,因为在以后我们有可能想修改那个实现。我们可能为了效率这样做,或因为对问题有了更好的理解,或因为有些新类变得可用而想在实现里使用这些新类。任何危害实现隐蔽性的东西都会减少语言的灵活性。这样,内联函数就显得非常重要,因为它实际上消除了预处理器宏和伴随它们的问题。通过用内联函数方式,成员函数可以和预处理器宏一样有效。

当然,内联函数也许会在类定义里被多次使用。因为它更简单,所以程序设计者都会这样做。但这不是大问题,因为以后期待程序规模减少时,可以将函数移出内联而不影响它们的功能。开发指南应该是“首先是使它起作用,然后优化它。”

二.相关代码

1.

<span style="font-size:18px;"><strong>/*MACPO.cpp*/
#include <fstream.h>
#define band(x) (((x)>5 && (x)<10)?(x):0)
//但当数字在范围之内时,两个表达式都测试,产生两次自增操作。产生这个结果是
//由于再次对参数操作。一旦数字出了范围,两个条件仍然测试,所以也产生两次自
//增操作。根据参数不同产生的副作用也不同。
int main()
{
	ofstream 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;
	}

	return 0;
}</strong></span>

2.

<span style="font-size:18px;"><strong>/*存取函数
在类中内联函数的最重要的用处之一是用于一种叫存取函数的函数。这是一个小函数,它
容许读或修改对象状态—即一个或几个内部变量。*/
/*ACCESS.cpp*/
#include <iostream>
using namespace std;

class access
{
	int i;
public:
	int read() const
	{
		return i;
	}
	void set(int I)
	{
		i = I;
	}
};

int main()
{
	access A;
	A.set(100);
	int x = A.read();

	return 0;
}</strong></span>

3.

<span style="font-size:18px;"><strong>/*存取器( accessors )和修改器( mutators )
一些人进一步把存取函数的概念分成存取器(从一个对象读状态信息)和修改器(修改状态信息)。而
且,可以用重载函数对存取器和修改器提供相同名字的函数,如何调用函数决定了我们是读还是修改状
态信息。*/
/*RECTANGL.cpp*/
#include <iostream>
using namespace std;

class rectangle
{
	int Width, Height;
public:
	rectangle(int W = 0, int H = 0):Width(W), Height(H)
	{}
	int width() const//read
	{
		return Width;
	}
	void width(int W)//set
	{
		Width = W;
	}
	int height() const//read
	{
		return Height;
	}
	void height(int H)//set
	{
		Height = H;
	}
};

int main()
{
	rectangle R(19, 47);
	R.height(2*R.width());
	R.width(2*R.height());

	return 0;
}</strong></span>

4.

<span style="font-size:18px;"><strong>/*一个内联函数对于一个还没有在类里声明的函数进行向前引用,编
译器就可能不能处理它*/
/*ECORDER.cpp*/
#include <iostream>
using namespace std;

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

int main()
{
	forward F;
	F.f();

	return 0;
}
/*虽然函数g()还没有定义,但在函数 f()里对函数g()进行了调用。这是可行的,因
为语言定义规定非内联函数直到类声明结束才赋值。当然,函数g()也调用函数f(),
我们将得到一组递归调用,这些递归对于编译器进行内联是过于复杂了。(应该在函
数f()或g()里也执行一些测试来强迫它们之一“停止”,否则递归将是无穷的)。*/</strong></span>

5.

<span style="font-size:18px;"><strong>/*在构造函数和析构函数里隐藏行为
构造函数和析构函数是两个使我们易于认为内联比它实际上更有效的函数。构造函数和析
构函数都可能隐藏行为,因为类可以包含子对象,子对象的构造函数和析构函数必须被调用。
这些子对象可能是成员对象,或可能由于继承(继承还没有介绍)而存在。下面是一个有成员
对象的例子*/
/*HIDDEN.cpp*/
#include <iostream>
using namespace std;

class member
{
	int i, j, k;
public:
	member(int x = 0)
	{
		i = j = k = x;
	}
	~member()
	{
		cout << "~member" << endl;
	}
};

class withMembers
{
	member Q, R, S;
	int i;
public:
	withMembers(int I):i(I)
	{}
	~withMembers()
	{
		cout << "~withMembers" <<endl;
	}
};

int main()
{
	withMembers WM(1);

	return 0;
}
/*在类withMembers里,内联的构造函数和析构函数看起来似乎很直接和简单,但其实很复
杂。成员对象Q、 P和S的构造函数和析构函数被自动调用,这些构造函数和析构函数也是内联
的,所以它们和普通的成员函数的差别是显著的。这并不是意味着应该使构造函数和析构函数
定义为非内联的。一般说来,快速地写代码来建立一个程序的初始“轮廓”时,使用内联函数
经常是便利的。但假如要考虑效率,内联是值得注意的一个问题。*/</strong></span>

6.

<span style="font-size:18px;"><strong>/*假如想优化,那么使用关键字inline。使用这个方法,前面 RECTANGL.CPP例子修改
如下:*/
/*NOINSITU.cpp*/
#include <iostream>
using namespace std;

class rectangle
{
	int Width, Height;
public:
	rectangle(int W = 0, int H = 0);
	int width() const;
	void width(int W);
	int height() const;
	void height(int H);
};

inline rectangle::rectangle(int W, int H):Width(W), Height(H)
{}

inline int rectangle::width() const//read
{
	return Width;
}

inline void rectangle::width(int W)//set
{
	Width = W;
}

inline int rectangle::height() const//read
{
	return Height;
}

inline void rectangle::height(int H)//set
{
	Height = H;
}

int main()
{
	rectangle R(19, 47);
	R.height(2*R.width());
	R.width(2*R.height());

	return 0;
}</strong></span>

7.

<span style="font-size:18px;"><strong>/*使用标准的C库函数中的时间函数来生成简单的 Time类*/
/*CPPTIME.h*/
#ifndef TEXT_H_
#define TEXT_H_
#include <time.h>
#include <string.h>

class Time
{
	time_t T;
	tm local;
	char Ascii[26];
	unsigned char lflag, aflag;
	void updateLocal()
	{
		if(!lflag)
		{
			local = *localtime(&T);
			lflag++;
		}
	}
	void updateAscii()
	{
		if(!aflag)
		{
			updateLocal();
			strcpy(Ascii, asctime(&local));
			aflag++;
		}
	}
public:
	Time()
	{
		mark();
	}
	void mark()
	{
		lflag = aflag = 0;
		time(&T);
	}
	const char* ascii()
	{
		updateAscii();
		return Ascii;
	}
	int delta(Time* dt) const
	{
		return difftime(T, dt->T);
	}
	int DaylightSavings()
	{
		updateLocal();
		return local.tm_isdst;
	}
	int DayOfYear()
	{
		updateLocal();
		return local.tm_yday;
	}
	int DayOfWeek()
	{
		updateLocal();
		return local.tm_wday;
	}
	int Since1900()
	{
		updateLocal();
		return local.tm_year;
	}
	int Month()
	{
		updateLocal();
		return local.tm_mon;
	}
	int DayOfMonth()
	{
		updateLocal();
		return local.tm_mday;
	}
	int Hour()
	{
		updateLocal();
		return local.tm_hour;
	}
	int Minute()
	{
		updateLocal();
		return local.tm_min;
	}
	int Second()
	{
		updateLocal();
		return local.tm_sec;
	}
};
#endif
/*两个私有函数updateLocal( )和 updateAscii( )检查标
记,并有条件地执行更新。*/</strong></span>

<span style="font-size:18px;"><strong>#include <iostream.h>
#include "CPPTIME.h"
/*在这个例子里,一个 Time对象被创建,然后执行一些时延动作,接着创建第 2个
Time对象来标记结束时间。这些用于显示开始时间、结束时间和消耗的时间。*/
int main()
{
	Time start;
	for(int i = 1; i < 1000; ++i)
	{
		cout << i << ' ';
		if(i%10 == 0)
		{
			cout << endl;
		}
	}
	Time end;
	cout << endl;
	cout << "start = " << start.ascii();
	cout << "end = " << end.ascii();
	cout << "delta = " << end.delta(&start);

	return 0;
}</strong></span>

8.

<span style="font-size:18px;"><strong>/*这是预处理器仍然有用的另一个例子,因为 _ FILE_和_LINE_指示仅和预处理器一
起起作用并用在assert( )宏里。假如assert( )宏在一个错误函数里被调用,它仅打
印出错函数的行号和文件名字而不是调用错误函数。这儿显示了使用宏联接(许多是
assert()方法)函数的方法,紧接着调用assert()(程序调试成功后这由一个#define
 NDEBUG消除)。*/
/*ALLEGE.h*/
#ifndef ALLEGE_H_
#define ALLEGE_H_
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

inline void allege_error(int val, const char* msg)
//函数allege_error()有两个参数:一个是整型表达式的值,另一个是这个值为
//false时需打印的消息
{
	if(!val)
	{
		fprintf(stderr, "error: %s\n", msg);
		//函数fprintf()代替iostreams是因为在只有少量错误的情况下,它工作得
		//更好。假如这不是为调试建立的,exit(1)被调用以终止程序。
#ifdef NDEBUG
		exit(1);
#endif
	}
}

//allege()宏使用三重if-then-else强迫计算表达式expr求值。在宏里调用了
//allege_error(),接着是assert(),所以我们能在调试时获得 assert()的好处
//——因为有些环境紧密地把调试器和assert()结合在一起。
#define allege(expr, msg){	allege_error((expr)?1:0, msg);		assert(expr);}

#define allegemem(expr)	allege(expr, "out of memory")
//allegefile( )宏是allege( )宏用于检查文件的专用版本

#define allegefile(expr)    allege(expr, "could not open file")
//allegemen()宏是allege( )宏用于检查内存的专用版本

#endif
</strong></span>

<span style="font-size:18px;"><strong>/*ERRTEST.cpp*/
//#define NDEBUG//Turn off asserts
#include "ALLEGE.h"
#include <fstream.h>

int main()
{
	int i = 1;
	allege(i, "value must be nonzero");
	void* m = malloc(100);
	allegemem(m);
	ifstream nofile("nofile.xxx");
	allegefile(nofile);

	return 0;
}</strong></span>

三.习题+解答

1. 将第7章练习2例子增加一个内联构造函数和一个称为 Print( )的内联成员函数,这个函数用于打印所有数组的值。

<span style="font-size:18px;"><strong>#include <iostream>
using namespace std;

class A
{
	const int i,j;
	enum
	{
		size = 100
	};
	unsigned char arr[size];
	int number;
public:
	A();
	A(int Number):i(0),j(0)
	{
		number = Number;
	}
	void Print()
	{
		for(int k = 0; k < size; ++k)
		{
			cout << arr[k] << ",";
		}
		cout << endl;
	}
};

A::A():i(0),j(0)
{
	for(int k = 0,m = '0'; k < size; ++k)
	{
		arr[k] = m + k;
	}
};

int main()
{
	A a;
	a.Print();

	return 0;
}</strong></span>

2. 对于第 3章的NESTFRND.CPP例子,用内联函数代替所有的成员函数使它们成为非in situ内联函数。同时再把initialize()函数改成构造函数。

<span style="font-size:18px;"><strong>#include <iostream>
using namespace std;

/*nestfrnd.cpp*/
#include <string.h>
#define SZ 20

/*struct holder包含一个整型数组和一个 p ointer,我们可以通过 pointer来存取这些整数。因为
pointer与holder紧密相连,所以有必要将它作为 struct中的一个成员。一旦 pointer被定义,它就
可以通过下面的声明来获得存取 holder的私有成员的权限:
friend holder : :pointer ;
注意,这里struct关键字并不是必须的,因为编译器已经知道 pointer是什么了*/

class holder
{
private:
	int a[SZ];
public:
	holder();
	class pointer
	{
	private:
		holder* h;
		int* p;
	public:
		pointer(holder* H);
	    void next();
		void previous();
		void top();
		void end();
		int read();
		void set(int i);
	};
	friend pointer;
};

inline holder::holder()
{
	memset(a,0,SZ*sizeof(int));
}

inline holder::pointer::pointer(holder* H)
{
	h = H;
	p = h->a;
}

inline void holder::pointer::next()
{
	if(p < &(h->a[SZ-1]))
	{
		p++;
	}
}

inline void holder::pointer::previous()
{
	if(p > &(h->a[0]))
	{
		p--;
	}
}

inline void holder::pointer::top()
{
	p = &(h->a[0]);
}

inline void holder::pointer::end()
{
	p = &(h->a[SZ-1]);
}

inline int holder::pointer::read()
{
	return *p;
}

inline void holder::pointer::set(int i)
{
	*p = i;
}

int main()
{
	holder h;
	holder::pointer hp1(&h);
	holder::pointer hp2(&h);
	int i;

	for(i = 0;i < SZ;++i)
	{
		hp1.set(i);
		hp1.next();
	}
	hp1.top();
	hp2.end();
	for(i = 0;i < SZ;++i)
	{
		cout<<"hp1 = "<<hp1.read()<<","
			<<"hp2 = "<<hp2.read()<<endl;

		hp1.next();
		hp2.previous();
	}

	return 0;
}</strong></span>

3. 使用第6章NL.CPP,在它自己的头文件里,将nl转变为内联函数。

STL源码

<span style="font-size:18px;"><strong>#if     _MSC_VER > 1000
#pragma once
#endif

#ifdef  __cplusplus

#ifndef _INC_OSTREAM
#define _INC_OSTREAM

#if     !defined(_WIN32) && !defined(_MAC)
#error ERROR: Only Mac or Win32 targets supported!
#endif

#ifdef  _MSC_VER
// Currently, all MS C compilers for Win32 platforms default to 8 byte
// alignment.
#pragma pack(push,8)

#include <useoldio.h>

#endif  // _MSC_VER

/* Define _CRTIMP */

#ifndef _CRTIMP
#ifdef  _DLL
#define _CRTIMP __declspec(dllimport)
#else   /* ndef _DLL */
#define _CRTIMP
#endif  /* _DLL */
#endif  /* _CRTIMP */

#include <ios.h>

#ifdef  _MSC_VER
// C4514: "unreferenced inline function has been removed"
#pragma warning(disable:4514) // disable C4514 warning
// #pragma warning(default:4514)        // use this to reenable, if desired
#endif  // _MSC_VER

typedef long streamoff, streampos;

class _CRTIMP ostream : virtual public ios {

public:
        ostream(streambuf*);
        virtual ~ostream();

        ostream& flush();
        int  opfx();
        void osfx();

inline  ostream& operator<<(ostream& (__cdecl * _f)(ostream&));
inline  ostream& operator<<(ios& (__cdecl * _f)(ios&));
        ostream& operator<<(const char *);
inline  ostream& operator<<(const unsigned char *);
inline  ostream& operator<<(const signed char *);
inline  ostream& operator<<(char);
        ostream& operator<<(unsigned char);
inline  ostream& operator<<(signed char);
        ostream& operator<<(short);
        ostream& operator<<(unsigned short);
        ostream& operator<<(int);
        ostream& operator<<(unsigned int);
        ostream& operator<<(long);
        ostream& operator<<(unsigned long);
inline  ostream& operator<<(float);
        ostream& operator<<(double);
        ostream& operator<<(long double);
        ostream& operator<<(const void *);
        ostream& operator<<(streambuf*);
inline  ostream& put(char);
        ostream& put(unsigned char);
inline  ostream& put(signed char);
        ostream& write(const char *,int);
inline  ostream& write(const unsigned char *,int);
inline  ostream& write(const signed char *,int);
        ostream& seekp(streampos);
        ostream& seekp(streamoff,ios::seek_dir);
        streampos tellp();

protected:
        ostream();
        ostream(const ostream&);        // treat as private
        ostream& operator=(streambuf*); // treat as private
        ostream& operator=(const ostream& _os) {return operator=(_os.rdbuf()); }
        int do_opfx(int);               // not used
        void do_osfx();                 // not used

private:
        ostream(ios&);
        ostream& writepad(const char *, const char *);
        int x_floatused;
};

inline ostream& ostream::operator<<(ostream& (__cdecl * _f)(ostream&)) { (*_f)(*this); return *this; }
inline ostream& ostream::operator<<(ios& (__cdecl * _f)(ios& )) { (*_f)(*this); return *this; }

inline  ostream& ostream::operator<<(char _c) { return operator<<((unsigned char) _c); }
inline  ostream& ostream::operator<<(signed char _c) { return operator<<((unsigned char) _c); }

inline  ostream& ostream::operator<<(const unsigned char * _s) { return operator<<((const char *) _s); }
inline  ostream& ostream::operator<<(const signed char * _s) { return operator<<((const char *) _s); }

inline  ostream& ostream::operator<<(float _f) { x_floatused = 1; return operator<<((double) _f); }

inline  ostream& ostream::put(char _c) { return put((unsigned char) _c); }
inline  ostream& ostream::put(signed char _c) { return put((unsigned char) _c); }

inline  ostream& ostream::write(const unsigned char * _s, int _n) { return write((char *) _s, _n); }
inline  ostream& ostream::write(const signed char * _s, int _n) { return write((char *) _s, _n); }

class _CRTIMP ostream_withassign : public ostream {
        public:
                ostream_withassign();
                ostream_withassign(streambuf* _is);
                ~ostream_withassign();
    ostream& operator=(const ostream& _os) { return ostream::operator=(_os.rdbuf()); }
    ostream& operator=(streambuf* _sb) { return ostream::operator=(_sb); }
};

extern ostream_withassign _CRTIMP cout;
extern ostream_withassign _CRTIMP cerr;
extern ostream_withassign _CRTIMP clog;

inline _CRTIMP ostream& __cdecl flush(ostream& _outs) { return _outs.flush(); }
inline _CRTIMP ostream& __cdecl endl(ostream& _outs) { return _outs << '\n' << flush; }
inline _CRTIMP ostream& __cdecl nl(ostream& _outs) { return _outs << '\n'; }
inline _CRTIMP ostream& __cdecl ends(ostream& _outs) { return _outs << char('\0'); }

_CRTIMP ios&           __cdecl dec(ios&);
_CRTIMP ios&           __cdecl hex(ios&);
_CRTIMP ios&           __cdecl oct(ios&);

#ifdef  _MSC_VER
// Restore default packing
#pragma pack(pop)
#endif  // _MSC_VER

#endif  // _INC_OSTREAM

#endif  /* __cplusplus */
</strong></span>

4. 创建一个类A,具有能自我宣布的缺省构造函数。再写一个新类 B, 将A的一个对象作为B的成员,并为类B写一个内联构造函数。创建一个 B对象的数组并看看发生了什么事。

#include <iostream>
using namespace std;

class A
{
	int i;
public:
	A(int I = 0);
	~A()
	{
		cout << "Delete A!" << endl;
	}
};

A::A(int I):i(I)
{
	cout << "Create A!" <<endl;
}

class B
{
	A a;
	enum{
		size = 10
	};
	int arr[size];
public:
	friend A;
	B()
	{
		a = 0;
		for(int k = 0,m = 0;k < size; ++k,++m)
		{
			arr[k] = m;
		}
		cout << "Create B!" << endl;
	}
	~B()
	{
		cout << "Delete B!" << endl;
	}
};

int main()
{
	B b;

	return 0;

}

输出结果如下:

5. 从练习4里创建大量的对象并使用 Time类来计算非内联构造函数和内联构造函数之间的时间差别(假如我们有剖析器,也试着使用它。)

对于大量对象无法很快得出,故加入了非内联函数和内联函数:

/*使用标准的C库函数中的时间函数来生成简单的 Time类*/
/*CPPTIME.h*/
#ifndef TEXT_H_
#define TEXT_H_
#include <time.h>
#include <string.h>
#include <iostream>
using namespace std;

class A
{
	int i;
	char c;
	float f;
	double d;
	int j;
	char n;
	float m;
	double g;
public:
	A()
	{
		i = j = 0;
		c = n = '0';
		f = m = 1.0;
		d = g = 0.0;
		cout << "Create A()!" << endl;
	}
	A(int I, char C, float F, double D, int J, char N, float M, double G);
};

A::A(int I, char C, float F, double D, int J, char N, float M, double G)
:i(I),c(C),f(F),d(D),j(J),n(N),m(M),g(G)
{
	cout << "Create A(int I, char C, float F, double D, int J, char N, float M, double G)!"
		<< endl;
}

class B
{
	A a;
	int flag;
	char name;
	double number;
	enum{
		size = 10
	};
	int arr[size];
public:
	friend A;
	B()
	{
		flag = 0;
		name = '0';
		number = 0.0;
		for(int k = 0,m = 0;k < size; ++k,++m)
		{
			arr[k] = m;
		}
		cout << "Create B()!" << endl;
	}
	B(int Flag, char Name, double Number);
	void g();
	void f()
	{
		cout << "f()" << " ";
	}
};

B::B(int Flag, char Name, double Number):flag(Flag),name(Name),number(Number)
{
	cout << "Create B(int Flag, char Name, double Number)!"
		<< endl;
}

void B::g()
{
	cout << "g()" << " ";
}

class Time
{
	time_t T;
	tm local;
	char Ascii[26];
	unsigned char lflag, aflag;
	void updateLocal()
	{
		if(!lflag)
		{
			local = *localtime(&T);
			lflag++;
		}
	}
	void updateAscii()
	{
		if(!aflag)
		{
			updateLocal();
			strcpy(Ascii, asctime(&local));
			aflag++;
		}
	}
public:
	Time()
	{
		mark();
	}
	void mark()
	{
		lflag = aflag = 0;
		time(&T);
	}
	const char* ascii()
	{
		updateAscii();
		return Ascii;
	}
	int delta(Time* dt) const
	{
		return difftime(T, dt->T);
	}
	int DaylightSavings()
	{
		updateLocal();
		return local.tm_isdst;
	}
	int DayOfYear()
	{
		updateLocal();
		return local.tm_yday;
	}
	int DayOfWeek()
	{
		updateLocal();
		return local.tm_wday;
	}
	int Since1900()
	{
		updateLocal();
		return local.tm_year;
	}
	int Month()
	{
		updateLocal();
		return local.tm_mon;
	}
	int DayOfMonth()
	{
		updateLocal();
		return local.tm_mday;
	}
	int Hour()
	{
		updateLocal();
		return local.tm_hour;
	}
	int Minute()
	{
		updateLocal();
		return local.tm_min;
	}
	int Second()
	{
		updateLocal();
		return local.tm_sec;
	}
};
#endif
/*两个私有函数updateLocal( )和 updateAscii( )检查标
记,并有条件地执行更新。*/

#include "CPPTIME.h"
/*在这个例子里,一个 Time对象被创建,然后执行一些时延动作,接着创建第 2个
Time对象来标记结束时间。这些用于显示开始时间、结束时间和消耗的时间。*/
int main()
{
	Time start1;
	A a;
	B b;
	for(int i = 0;i < 1000; ++i)
	{
		b.f();
	}
	Time end1;
	cout << endl;
	cout << "start1 = " << start1.ascii();
	cout << "end1 = " << end1.ascii();
	cout << "delta1 = " << end1.delta(&start1) << endl;
	Time start2;
	A d(1,'1',1.0,2.0,2,'2',3.0,4.0);
	B c(1,'0',1.0);
	for(i = 0; i < 1000; ++i)
	{
		c.g();
	}
	Time end2;
	cout << endl;
	cout << "start2 = " << start2.ascii();
	cout << "end2 = " << end2.ascii();
	cout << "delta2 = " << end2.delta(&start2) << endl;

	return 0;
}

测试结果:

前者为内联函数+内联构造函数;后者为非内联函数+非内联构造函数。

可知当函数内部较为复杂时,内联函数的效率低,应尽量避免使用。

以上代码仅供参考,如有错误请大家指出,谢谢大家~

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-07 21:18:22

《C++编程思想》第八章 内 联 函 数 (知识点+习题+解答)的相关文章

《C++编程思想》 第十一章 运算符重载 (习题+解答)

一.相关代码 1. /*运算符重载语法*/ /*OPOVER.cpp*/ /*这两个重载的运算符被定义为内联成员函数.对于二元运算符,单个参数是出现在运算符 右侧的那个.当一元运算符被定义为成员函数时,没有参数.成员函数被运算符左侧的对象调 用. 对于非条件运算符(条件运算符通常返回一个布尔值),如果两个参数是相同的类型,希 望返回和运算相同类型的对象或引用.如果它们不是相同类型,它作什么样的解释就取决于程 序设计者.用这种方法可以组合复杂的表达式: K += I + J ; 运算符+号产生一个

《C++编程思想》 第七章 常 量 (习题+解答)

一.相关知识点        在 C语言中可以选择这样书写: const bufsize:        这样写在C++中是不对的,而 C编译器则把它作为一个声明,这个声明指明在别的地方有存储分配.因为C默认const是外部连接的, C++默认const是内部连接的,这样,如果在 C++中想完成与C中同样的事情,必须用extern把连接改成外部连接: extern const bufsize;//declaration only 这种方法也可用在C语言中. 指向const的指针 使用指针定义的技

《C++编程思想》第三章 隐藏实现 (习题+解答)

一.相关知识点 1.  在C++中,存取控制并不是面向对象的特征,但它为类的创建者提供了很有价值的访问控制.类的用户可以清楚地看到,什么可以用,什么应该忽略.更重要的是,它保证了类的用户不会依赖任何类的实现细节.有了这些,我们就能更改类的实现部分,没有人会因此而受到影响,因为他们并不能访问类的这一部分.一旦我们有了更改实现部分的自由,就可以在以后的时间里改进我们的设计,而且允许犯错误.要知道,无论我们如何小心地计划和设计,都可能犯错误.知道犯些错误也是相对安全的,这意味着我们会变得更有经验,会学

《C++编程思想》 第十章 引用和拷贝构造函数(知识点+习题+解答)

一.相关知识点 使用引用时有一定的规则: 1) 当引用被创建时,它必须被初始化.(指针则可以在任何时候被初始化.) 2) 一旦一个引用被初始化为指向一个对象,它就不能被改变为对另一个对象的引用.(指针则可以在任何时候指向另一个对象.) 3) 不可能有NULL引用.必须确保引用是和一块合法的存储单元关连. 仅当准备用传值的方式传递类对象时,才需要拷贝构造函数.如果不需要这么做,就不要拷贝构造函数. 对于指向一个对象的指针新的语法变为 ->*,对于一个对象或引用则为.*. 二.相关代码 1. /*R

《C++编程思想》 第九章 命 名 控 制 (知识点+习题+解答)

一.相关知识点         那些通常放在头文件里的名字,像常量.内联函数(inline function),在缺省情况下都是内部连接的(当然常量只有在C + +中缺省情况下是内部连接的,在 C中它缺省为外部连接).注意连接只引用那些在连接/装载期间有地址的成员,因此类声明和局部变量并没有连接. 名字空间的产生与一个类的产生非常相似: namespace MyLib{ //Declarations } 这就产生了一个新的名字空间,其中包含了各种声明.namespace与class.struct

Java编程思想---第八章 多态(上)

第八章  多态(上) 在面向对象的程序设计语言中,多态是继数据抽象和继承之后的第三种基本特征. 多态通过分离做什么和怎么做,从另一角度将接口和实现分离开来,多态不但能改善代码的组织结构和可读性,还能够创建可扩展的程序,无论在项目最初创建时还是在需要添加新功能时都可以生长程序.封装通过合并特征和行为来创建新的数据类型. 8.1 再论向上转型 在上一章中我们已经知道,对象可以作为它本身的类型使用,也可以作为它的基类使用,而这种把对某个对象的引用视为对其基类的引用的做法被称为向上转型. //: dem

java编程思想第八章多态

前言: 封装:通过合并特征和行为创建新的数据类型. 实现隐藏:通过将细节"私有化",把接口和实现分离. 多态:消除类型间的耦合关系.也称作动态绑定,后期绑定或运行时绑定. 8.1再论向上转型: 对象既可以作为本身的类型使用,也可以作为基类的类型使用.这种做法成为向上转型. 其代码表现就是:父类引用指向子类. 多态允许在参数的地方使用,即参数处声明基类,用于接受基类或导出类实现. 8.2转机(主要是介绍如何实现多态) 为了解决多态问题,在java中提供了后期绑定的方法,即在运行时根据对象

32位汇编第七讲,混合编程,内联汇编

32位汇编第七讲,混合编程 博客园IBinary原创 QQ:2510908331  博客连接:http://www.cnblogs.com/iBinary/ 转载请注明出处,谢谢 混合编程的概念,有时候我们会想,C语言可不可以调用汇编的函数,或者反过来调用 或者说C语言里面内部直接内联汇编去编写. 可以实现,静看怎么实现 一丶C语言调用汇编语言的函数 1.创建工程和代码 ①创建VC++控制台程序 FILE(文件) - NEW (新建) 然后我们打开源文件,一级一级展开,找到我们的main函数 那

标准C++编程笔记day01 内联、new/delete、引用、转换

内联函数(inline):用函数已被编译好的二进制代码替换对函数的调用指令. 只有频繁调用的简单函数才适合内联,稀少被调用的复杂函数和递归函数都不适合内联. 编译生成.s的汇编代码:g++ -S ***.cpp 直接输出指针a地址值:count<<(void*)a; 以数组形式new的内存指针 int *a=new int [10];,也要以数组形式去delete,如 delete a[]; 当指针为空时,delete 不会出错 ,但指针为野指针时,delete会出错. 标准异常处理: try