一步一步学习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、当同一类的所有对象使用一个量时,对于这个共用的量,可以用静态数据成员变量,这个变量对于同一类的所有的对象都取相同的值。静态成员变量只能被静态成员函数调用。静态成员函数也是由同一类中的所有对象共用。只能调用静态成员变量和静态成员函数。

时间: 2024-10-19 13:21:39

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

初探swift语言的学习笔记(类对象,函数)

swift扩展了很多功能和属性,有些也比较奇P.只有慢慢学习,通过经验慢慢总结了. 下面将初步学习一下类的写法. 码工,最大爱好就是看码,而不是文字,太枯燥. // // computer.swift // swiftDemo // // Created by apple on 14-6-8. // Copyright (c) 2014年 fengsh. All rights reserved. /* 写本例子的目的在于快速学习swift类的写法,包括知识点: 1.属性设置 2.构造.释构 3.

类的成员函数指针(比較深入)

From:http://blog.csdn.net/hairetz/archive/2009/05/06/4153252.aspx 个人感觉对于类的成员函数指针这块解说的比較深入具体 推荐阅读 ///////////////////////////////////////////////// 先看这样一段代码 class test {    public:       test(int i){ m_i=i;}       test(){} void hello()       {        

(转)c++类的成员函数存储方式(是否属于类的对象)---一道面试题引发的思考

昨天去面试一家公司,面试题中有一个题,自己没弄清楚,先记录如下: class D { public: void printA() { cout<<"printA"<<endl; } virtual void printB() { cout<<"printB"<<endl; } }; main函数调用: D *d=NULL; d->printA(); d->printB(); 输出结果是? 当时想的是对象d直

作为类的成员函数,重载运算符只能有一个参数

1 overload a operator of a class, you can only use one para., this pointer is automatically used. class Rational { public: //not correct since this ponit would be used automatically. //Rational operator+ (const Rational& lhs, const Rational& rhs);

类的成员函数实现线程的回调函数

一般都是用静态函数作为线程的回调函数实现,但是总是感觉不是很顺畅,更改吧,就好像破坏了类的封装性,不改吧,访问实在是麻烦.所以,今天要做的就是让类的成员函数作为线程的回调函数存在,其中使用的一个比较特殊的结构就是 union { void ( *ThreadProc)(LPVOID pvParam); void ( student::*MemberProc)(LPVOID pvParam); } Proc; 联合类,用于转换类成员方法指针到普通函数指针 下面是一个小李子,变量名 就凑活看吧,核心

类的成员函数后面加const有什么用(c++常问问题六)

每个类的成员函数都默认传入this指针,成员函数后面加了const后该成员函数将不能修改该类的成员了 class cat { public: cat(){}; string getName() const { this->m_strName = “”;//错误,const this不允许修改成员 return this->m_strName; //正确,没修改 } protected: string m_strName; }

C++类的成员函数使用的一些小总结

From: http://blog.csdn.net/xiayefanxing/article/details/7607506 这一阵做项目代码开发的时候,用到了在一个C++文件中使用另一个类的成员函数的问题,做个小总结. 其中有些是网上搜索的资料,因为比较分散就不一一给出出处了,请作者见谅. 1.C++如何在一个类的成员函数中调用另一个类的成员函数? 假设你想在类A里调用类B的函数int f(x),两种办法: (1)class A::B 也就是说将B定义为A的父类, 这样你就可以自然的在A里面

const修饰类的成员函数

<Effective C++>里面说,尽量使用const,const修饰变量一般有两种方式:const T *a,或者 T const *a,这两者都是一样的,主要看const位于*的左边还是右边,这里不再赘述,主要来看一下当const修饰类的成员函数时,成员函数有什么特点. 类的成员函数后面加 const,表明这个函数不会对这个类对象的数据成员(准确地说是非静态数据成员)作任何改变. 在设计类的时候,一个原则就是对于不改变数据成员的成员函数都要在后面加 const,而对于改变数据成员的成员函

处理菱形继承问题&&实现一个虚函数的覆盖及调用&&实现以下几个类的成员函数

#include <iostream> #include <string> using namespace std; 1.实现以下几个类的成员函数 2.实现一个虚函数的覆盖及调用 3.处理菱形继承问题. 植物 class Botany { public: //(const string& name) // const char* name Botany(const char* name = "") :_name(name) //构造函数 { //cout