一步一步学习C++(类)之成员函数的特性

在类体中说明的函数作为类的成员,称为成员函数。一般的成员函数,它是根据某种类的功能的需要来定义的。除此之外,又讨论了一些特殊的成员函数:构造函数、析构函数、拷贝初始化构造函数等。本节讨论除成员函数定义与说明之外的其它一些特殊属性。

一.内联函数和外联函数

类的成员函数可分为内联函数与外联函数。内联函数是指定义在类体内的成员函数,即该函数的定义放在类的体内。而对成员函数的说明放在体内,其函数的定义放在体外称之为外联函数。如果使外联函数转变为内联函数,只须在函数头部左端加上关键字inline即可。
内联函数在调用时并不发生程序执行的转移,而是在调用内联函数处用内联函数体的代码来替换,以节省调用开销,提高运行效率。

【说明】:函数是一种更高级的抽象。它的引入使得编程者只关心函数的功能和使用方法,而不必关心函数功能的具体实现;函数的引入可以减少程序的目标代码,实现程序代码和数据的共享。但是,函数调用也会带来降低效率的问题,因为调用函数实际上将程序执行顺序转移到函数所存放在内存中某个地址,将函数的程序内容执行完后,再返回到转去执行该函数前的地方。这种转移操作要求在转去前要保护现场并记忆执行的地址,转回后先要恢复现场,并按原来保存地址继续执行。因此,函数调用要有一定的时间和空间方面的开销,于是将影响其效率。特别是对于一些函数体代码不是很大,但又频繁地被调用的函数来讲,解决其效率问题更为重要。引入内联函数实际上就是为了解决这一问题。 
在程序编译时,编译器将程序中出现的内联函数的调用表达式用内联函数的函数体来进行替换。显然,这种做法不会产生转去转回的问题,但是由于在编译时将函数休中的代码被替代到程序中,因此会增加目标程序代码量,进而增加空间开销,而在时间代销上不象函数调用时那么大,可见它是以目标代码的增加为代价来换取时间的节省。 
在程序中,调用其函数时,该函数在编译时被替代,而不是像一般函数那样是在运行时被调用

使用内联函数应注意的事项 
内联函数具有一般函数的特性,它与一般函数所不同之处公在于函数调用的处理。一般函数进行调用时,要将程序执行权转到被调用函数中,然后再返回到调用它的函数中;而内联函数在调用时,是将调用表达式用内联函数体来替换。在使用内联函数时,应注意如下几点: 

1.一个函数可以自已调用自已,称为递归调用(后面讲到),含有递归调用的函数不能设置为inline;

2.使用了复杂流程控制语句:循环语句和switch语句,无法设置为inline;

3.由于inline增加体积的特性,所以建议inline函数内的代码应很短小。最好不超过5行。

4.inline仅做为一种“请求”,特定的情况下,编译器将不理会inline关键字,而强制让函数成为普通函数。出现这种情况,编译器会给出警告消息。

5.在你调用一个内联函数之前,这个函数一定要在之前有声明或已定义为inline,如果在前面声明为普通函数,而在调用代码后面才定义为一个inline函数,程序可以通过编译,但该函数没有实现inline。

总结为几句话

1.在内联函数内不允许用循环语句和开关语句。 
2.内联函数的定义必须出现在内联函数第一次被调用之前。

    只要声明为内联,编译器就不把它编译成一次函数调用,而只是类似于把函数的代码拷贝到被调用的地方,而且这完全是编译器私下里完成的,原来的访问权限等问题丝毫不受影响。这不是两全齐美了吗:在保证代码的面向对象性和结构化不受损失的条件下,程序的效率也没有损失。

Class   MyClass  
{  
public:  
	inline   int   GetState();  
private:  
	int   m_iState;  
}  
   
int inline  MyClass::GetState()  
{  
	return   m_iState;  
}  

内联函数还有另外一种写法,就是直接写在类中,此时,不必使用“inline”关键字。

class   MyClass  
{  
public:  
	int   GetState()
	{   
		return   m_iState;   
	}  
private:  
	int   m_iState;  
}; 
    内联函数只是一种编译机制,用上面两种形式声明的函数仅仅是建议编译器进行内联,而编译器是否内联不一定。正如前面所说,函数调用的开销只是对小的函数不可忽略,对于重量级的函数还是可以忽略的,而且在绝大多数的场合,函数调用才是人间正道,才是解决问题的最佳。所以大多数编译器并不把带有循环、递归等或者代码比较多的函数进行内联编译,有的甚至不允许声明成内联的。

二.静态成员

类是类型而并非数据对象,每个类的对象都是该类数据成员的拷贝。然而,在有的时候,需要类的所有对象在类的范围内共享某个数据。声明为static的类成员便能在类的范围内中共享,称之为静态成员。因此,静态成员的提出是为了解决数据共享问题。
http://blog.csdn.net/skyereeee/article/details/8000512

static主要有三个作用:

(1)局部静态变量

(2)外部静态变量/函数

(3)静态数据成员/成员函数
由于全局变量不属于类的成员,它的访问权限是完成开放的,因此,既不安全,又影响了重用性。
而静态成员具有上述问题的双重属性,不但数据可以得到封装,又可为所有的对象所共享。
静态数据成员是类中所有对象所共享的成员,而不是某个对象的成员。
静态数据成员的另一个优势是可以节省内存,对多个对象来说,静态数据成员只存储一处,为所有的对象所共用。
静态数据成员的值对每个对象都是一样的,但它的值可以更新。只要对静态数据成员的值更新一次,可保证所有对象存取更新后的相同值,这样可提高时间效率。
2.1静态成员的初始化在类的体外进行,而值得注意的是前面不加关键字static的意义在于避免与一般静态变量或对象相混淆。
class   MyClass  
{  
public:  
	int   GetState()
	{   
		return   m_iState;   
	}  
private:  
	int   m_iState;  
	static int a;//定义
} ;
<span style="color:#ff0000;">
</span>int MyClass::a = 0;//初始化

2.2 静态数据成员被 类 的所有对象所共享,包括该类派生类的对象。即派生类对象与基类对象共享基类的静态数据成员
// StaticForClass.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
class   MyClass  
{  
public:  
	int   GetState()
	{   
		return   m_iState;   
	}  
	static int a;	//定义
private:  
	int   m_iState;  	
}; 
int MyClass::a = 0;//初始化

class DerivedClass:public MyClass
{
	
};
int _tmain(int argc, _TCHAR* argv[])
{
	MyClass A;
	DerivedClass B;
	printf("%d\n",A.a);
	A.a++;
	printf("%d\n",B.a);
	getchar();
	return 0;
}<span style="color:#ff0000;">
</span>

三、静态成员函数

3.1 静态成员函数的地址可用普通函数指针储存,而普通成员函数地址需要用 类成员函数指针来储存
// StaticForClass.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"


class   MyClass  
{  
public:  
	static int   GetState();
	void print();
	static int a;	//定义
private:  
	int   m_iState;  
	
}; 
int MyClass::a = 0;//初始化

int MyClass::GetState()//静态函数的调用
{
	return a;
	//return m_iState;/* error C2597: 对非静态成员“MyClass::m_iState”的非法引用*/
}
void MyClass::print()
{
	printf("%d",a);
}

class DerivedClass:public MyClass
{
	
};

int _tmain(int argc, _TCHAR* argv[])
{
#if 0
	MyClass A;
	DerivedClass B;
	printf("%d\n",A.a);
	A.a++;
	printf("%d\n",B.a);
#endif
	int (*pf1)() = &MyClass::GetState;//普通的函数指针 
	//int (MyClass::*pf2)() = &MyClass::GetState;
	/*error C2440: “初始化”: 无法从“int (__cdecl *)(void)”转换为“int (__thiscall MyClass::* )(void)”*/
	void (MyClass::*pf3)() = &MyClass::print;//普通的函数指针 
	
	getchar();
	return 0;
}

3.2 静态成员函数没有this指针,.静态成员函数不可以调用类的非静态成员,它不能返回非静态成员,因为除了对象会调用它外,类本身也可以调用。
class   MyClass  
{  
public:  
	static int   GetState();//静态函数
	static int a;	//定义
private:  
	int   m_iState;  	
}; 
int MyClass::a = 0;//初始化
int MyClass::GetState()//静态函数的调用
{
	return a;
	//return m_iState;/* error C2597: 对非静态成员“MyClass::m_iState”的非法引用*/
}

关于静态成员函数,可以总结为以下几点:
1、出现在类体外的函数定义不能指定关键字static;
2、
静态成员之间可以相互访问,包括静态成员函数访问静态数据成员和访问静态成员函数;
3、非静态成员函数可以任意地访问静态成员函数和静态数据成员;
4、静态成员函数不能访问非静态成员函数和非静态数据成员;
5、由于没有this指针的额外开销,因此静态成员函数与类的全局函数相比速度上会有少许的增长;
6、调用静态成员函数,可以用成员访问操作符(.)和(->)为一个类的对象或指向类对象的指针调用静态成员函数
7、当同一类的所有对象使用一个量时,对于这个共用的量,可以用静态数据成员变量,这个变量对于同一类的所有的对象都取相同的值。静态成员变量只能被静态成员函数调用。静态成员函数也是由同一类中的所有对象共用。只能调用静态成员变量和静态成员函数。




郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。