类的特殊成员

静态变量

在C++中(以及其他一些语言,如 C#,Java 等面向对象的语言中)类的成员变量被声明为static(称为静态成员变量),意味着它为该类的所有实例所共享,也就是说当某个类的实例修改了该静态成员变量,其修改值为该类的其它所有实例所见。

  比如在某个类A中声明一个static int number;初始化为0。这个number就能被所有A的实例共用。在A的构造函数里加上number++,在A的析构函数里加上number--。那么每生成一个A的实例,number就加一,每销毁一个A的实例,number就减一,这样,number就可以记录程序中共生成了多少个A的实例。

  这只是静态成员的一种用法而已。

 

#include <iostream>
using namespace std;
class A
{
public:
	A(int num):it(num){total++;}
	static int total;          //声明一个静态成员变量total
	~A(){total--;}
	int get(){return it;}
	void set(int age){it=age;}
private:
	int it;
};
int A::total=0;          //一定不要忘记在全局定义该静态变量
int main()
{
	const int max=5;
	int i;
	A*a[max];
	for (i=0;i<max;i++)
	{
		a[i]=new A(i);
	}
	for (i=0;i<max;i++)
	{
		cout<<"该类有"<<A::total<<"个对象\n";
		cout<<"正在删除第"<<a[i]->get()<<"个对象\n";
		delete a[i];
	}
	return 0;

 

(1)静态成员变量属于整个类的全局变量,它不单数于某个对象,普通成员变量是属于对象的变量,他不能被所有对象共享。

(2)静态成员变量必须在全局进行定义,而普通成员变量由于是专属某个对象而不是一个类的,所以不需要在全局进行定义。

(3)在使用静态成员变量时,可以不将其限定为某个具体对象,只与类名连用即可。如A::total而不用A a;      a.total;

(4)静态成员在没有对象之前就存在。如:

 

#include <iostream>
using namespace std;
class A
{
public:
	static int n;
};
int A::n=0;
void show(){cout<<A::n<<"a \n";}
int main()
{
	int i;
	for (i=0;i<5;i++)
	{
		A::n++;
		show();
	}
	return 0;
}

 


私有静态成员变量

上面的静态成员都是公有的,因此程序中所有的函数都可以访问它,假如我们不想让所有的函数都可以访问它,则可以将它声明为私有,这样就只有通过该类的共有成员函数才能访问它,但前提是必须创建该类的一个对象。

 

#include <iostream>
using namespace std;
class A
{
public:
	void func(){cout<<A::x;}
private:
	static int x;
};
int A::x=1000;
int main()
{
	A a;
	a.func();
	return 0;
}


 

静态成员函数

 

公有的静态成员函数在未创建对象时也可以使用,他的调用与静态成员变量一样。

要注意的是,由于静态成员函数属于整个类,因此他不能访问某个对象的成员变量,他没有this指针指向该对象,不过他可以访问该类的静态成员变量

#include <iostream>
using namespace std;
class A
{
public:
	void static show(){cout<<A::n;n++;}
private:
	static int n;
};
int A::n=0;
int main()
{
	A a,b,c;
	a.show();
	b.show();
	c.show();
	return 0;
}

 

(1)要使用类成员限定符来访问静态成员,不要使用对象名,因为静态成员是类的成员而不是对象的成员。

(2)静态成员可以被继承,积累和派生类的对象都可以共享该静态成员,其他特性与一般成员类似。

(3)类中任何成员函数都可以访问静态成员,但是静态成员函数不能直接访问非静态成员,只能通过对象访问该对象的非静态成员。这是因为静态成员函数属于整个类,没有他只向某个对象的this指针。

 

静态成员函数不能被说明为虚函数
 

 

#include <iostream>
using namespace std;
class aspl         //将阿司匹林声明为一个aspl类,那么每箱阿司匹林就是该类的一个对象      
{
   public:
   aspl(float p){price=p;TotalPrice=p+TotalPrice;}    //在构造函数中实例化该对象的私有成员变量price,这样就得到了一箱阿司匹林并且有了它的初始价格
   ~aspl(){TotalPrice=TotalPrice-price;} 	//析构函数销毁该对象并且将总价格减去该箱的价格,这样账面上就少了一箱阿司匹林,并且总价格也减去了该箱的价格
   static float get(){return TotalPrice;}
   private:
   float price;        		//由于每箱阿司匹林都有价格,因此必须得有个成员变量来表示价格,这里在aspl这个类中声明一个私有成员变量price
   static float TotalPrice;  	//由于阿司匹林的总价格属于类的总价格,而不是某一箱阿司匹林的价格,因此我们要将总价格声明为静态成员变量,这里声明为TotalPrice
};
float aspl::TotalPrice=0;  	//静态成员变量必须初始化
void main()
{
   float f;
   cout<<"阿司匹林的库存总价格为:";
   cout<<aspl::get()<<endl;  	//必须用类名限定符来调用静态成员函数
   int i=0;
   cout<<"请输入第"<<i+1<<"次购进的阿司匹林的单箱价格:";
   cin>>f;
   aspl *p[5];          		//定义了5个指向aspl类的数组指针p
   p[i]=new aspl(f);   		//购进一箱阿司匹林
   cout<<"阿司匹林的库存总价格为:";
   cout<<aspl::get()<<endl;  	//输出总价格
   i++;          				//i代表购进的次数,i++表示将要进行i+1次购进
   cout<<"请输入第"<<i+1<<"次购进的阿司匹林的单箱价格:";  //提示用户输入i次购进
   cin>>f;
   p[i]=new aspl(f);  		//输入的数值保存在i次购进的对象的成员变量中
   cout<<"阿司匹林的库存总价格为:";
   cout<<aspl::get()<<endl; 	//输出当前的库存总价格
   cout<<"请输入卖出的阿司匹林的编号,编号即第几次购进:";//提示用户要删除哪次购进
   cin>>i;        				//将输入值保存在i变量中
   delete p[i];  				//删除第i次创建的对象
   cout<<"阿司匹林的库存总价格为:";
   cout<<aspl::get()<<endl;  	//再次输出销售一箱阿司匹林后的库存总价格
}

 

 

 

指针函数

 

数组名是指向数组第一个元素的常量指针,同理,函数名也是指向函数的第一条指令的常量指针。一般来说程序编译后,每个函数都有一个首地址,也就是函数第一条指令的地址,我们用一个指针来保存这个地址,那么这个指针就是函数指针,该指针可看作函数名,因此我们可以通过指针调用函数。

函数指针的声明方法:

int (*func)(int);

该语句声明了一个指针func他指向一个函数,这个函数带有一个int型参数并返回int值,这里我们要注意,一个指向函数的指针必须确保该函数被定义且分配了内存,否则它指向一个空地址,这是指针的大忌。

 

#include <iostream>
#include <string>
using namespace std;
bool check(string str) 	//检测是否是数字的函数,要注意该函数一定要放在调用函数的上面
{
   for(int i = 0;i<str.length();i++)
   if((str[i]>'9' || str[i]<'0')&&(str[i]!='.'))
   return false;
   return true;
}
float triangle(float &x,float &y)
{
   return x*y*0.5;
}
float rectangle(float &x,float &y)
{
   return x*y;
}
void Swap(float &x,float &y)
{
   float n;
   n=x;
   x=y;
   y=n;
}
void print(float &x,float &y)
{
   cout<<"长为:"<<x<<"  "<<"宽为:"<<y<<endl;
}
void get(float &a ,float &b)
{
   cout<<"请输入x的新值:";
   string str1;cin>>str1;
   while(!check(str1)) 	//调用检测数字函数,如果返回值为假,执行该循环,为真退出
   {
      cout<<"输入的不是数字,请重新输入!!!"<<endl;
      cin>>str1;
   }
   a = atof(str1.c_str()); 		//将字符串转换为浮点数
   cout<<"请输入y的新值:";
   string str2;cin>>str2;
   while(!check(str2)){
      cout<<"输入的不是数字,请重新输入!!!"<<endl;
      cin>>str2;
   }
   b = atof(str2.c_str());
}
int main()
{
   void(*p)(float &,float &); 		//声明一个函数指针p,该指针指向一个返回void值并且带有两个float参数的函数
   float(*fp)(float &, float &);		//声明一个函数指针fp,该指针指向一个返回float值并且带有两个float参数的函数
   bool quit=false;
   float a=2,b=3; 					//定义两个参数a和b的值
   int choice;   						//声明选择参数choice
   while(quit==false)
   {
      cout<<"(0)退出(1)设定长宽(2)三角形(3)矩形(4)交换长宽:";
      cin>>choice;
      switch(choice) 					//条件判断语句
      {
         case 1:
            p=get;						//用指针p来指向函数名get,该函数带有两个float参数并返回一个void值,与函数指针p的参数和类型相吻合
            break;
         case 2:
            fp=triangle;				//用指针fp来指向函数名triangle,该函数带有两个float参数并返回一个float值,与函数指针fp的参数和类型相吻合
            break;
         case 3:
            fp=rectangle;				//用指针fp来指向函数名rectangle,该函数带有两个float参数并返回一个float值,与函数指针fp的参数和类型相吻合
            break;
         case 4:
            p=Swap;       				//用指针p来指向函数名swap,该函数带有两个float参数并返回一个void值,与函数指针p的参数和类型相吻合
break;
            default:
            quit=true;
            break;
      }
      if(quit)break;
      if(choice==1||choice==4)			//假如选择了第1或者第4项
      {
         print(a,b);
         p(a,b);   		  				//调用函数指针p所指向的函数,该指针指向的是一个返回值为void的函数,由于不同的选项中将不同的函数名赋给了指针p,因此选择不同则调用的函数也不同
         print(a,b);
      }
      else if(choice==2||choice==3)		//假如选择了第2和第3项
      {
         print(a,b);
         cout<<"面积为:"<<fp(a,b)<<endl;	//调用函数指针fp所指向的函数,该指针指向的是一个返回值为float的函数,由于不同的选项中将不同的函数名赋给了指针fp,因此选择不同则调用的函数也不同
      }
   }
   return 0;
}



 

函数指针数组

 

函数指针的变量类型,变量个数,返回值类型必须与所要指向的函数相同

#include <iostream>
using namespace std;
void square(float &x,float &y){x=x*x;y=y*y;}   //该函数将参数x和y的平方再次赋给x和y
void cube(float &x,float &y){x=x*x*x;y=y*y*y;}   //该函数将参数x和y的立方再次赋给x和y
void print(float &x,float &y){cout<<"长:"<<x<<"\t"<<"宽:"<<y<<endl;}  //该函数输出x和y的值
void Swap(float &x,float &y){int z;z=x;x=y;y=z;}	//该函数交换参数x和y的值
int main()
{
   float a=2,b=3;				//定义并初始化float变量a和b的值为2和3
   char choice='0';
   int i;
   void(*p[5])(float &,float &);	//声明一个函数指针数组p,它有5个成员,每个指针成员都指向一个带有2个float型参数并且返回void值的函数
   for(i=0;i<5;i++)
   {
      cout<<"(0)退出(1)平方(2)立方(3)交换x和y的值:";
      cin>>choice;
      bool quit=false;
      switch(choice)
      {
         case '0':quit=true;break;
         case '1':p[i]=square;break;
         case '2':p[i]=cube;break;
         case '3':p[i]=Swap;break;
         default:p[i]=0;
      }
      if(quit)break;		//假如quit为真,跳出for循环
      if(p[i]==0)			//假如p[i]的值为0
      {
         cout<<"请输入一个从0到3之间的数字\n";
         i=i-1;			//输入一个非法数字,则将循环次数减1,也就是消去此次循环
         continue;		//返回到循环的开始处继续运行,也就是第13行
      }
      cout<<"第"<<i<<"次执行,到第次结束\n";
      cout<<"初始值\n";
      print(a,b);
      cout<<"现在调用函数指针数组p["<<i<<"]所指向的函数...\n";
      p[i](a,b); 			//调用函数指针数组中第i个指针成员所指向的函数
      cout<<"运算后\n";
      print(a,b);
   }
   return 0;
}


 

函数指针作为函数的参数

 

include <iostream>
using namespace std;
void square(int &x,int &y)
{
   x=x*x;
   y=y*y;
}
void cube(int &x,int &y)
{
   x=x*x*x;
   y=y*y*y;
}
void Swap(int &x,int &y)
{
   int z;
   z=x;
   x=y;
   y=z;
}
void print(void(*p)(int &x,int &y),int &x,int &y)	//该函数有3个参数,第1个是一个函数指针p,它指向的函数带有两个参数,并返回一个void值,另外还有两个int型引用x和y
{
   cout<<"执行函数前\n";
   cout<<"x:"<<x<<"\t"<<"y:"<<y<<endl;
   p(x,y);
   cout<<"执行函数后\n";
   cout<<"x:"<<x<<"\t"<<"y:"<<y<<endl;
}
int main()
{
   int a=2,b=3;
   char choice;
   bool quit=false;
   void (*p)(int &,int &);
//声明的p为一个函数指针,它所指向的函数带有两个参数并返回 一个void值
   while(quit==false)
   {
      cout<<"(0)退出(1)平方(2)立方(3)交换参数:";
      cin>>choice;
      switch(choice)
      {
         case '0':quit=true;
         case '1':p=square;break; 		//输入1,将函数名square的地址赋给p
         case '2':p=cube;break;		//输入2,将函数名cube的地址赋给p
         case '3':p=Swap;break;		//输入3,将函数名Swap的地址赋给p
         default:p=0;break;
      }
      if(quit==true)break;
      if(p==0)
      {
         cout<<"请输入0到3之间的数字\n";
         continue;
      }
      print(p,a,b);					//调用将函数指针作为参数的函数print
   }
   return 0;
}


 

 

使用typedef简化函数指针

 

函数指针的声明格式为:

void(*vp)(float &,float &);

使用typedef简化

typedef void (*vp)(float &,float &);

typedef在编程中有两个用途:一个是给变量娶一个易记且有意义的新名字,另一个就是简化一些比较复杂的类型变量。本例中简化了vp复制的函数指针声明,因此可以使用vp来代替(*)(float &,float &),这样vp就成了一个函数指针类型名,该类型的指针指向一个带有2个float型引用参数并返回oid的函数。

有了这个函数指针类型,那么以后的步骤就简化多了。比如:

vp p;

 

 

示例代码

 

#include <iostream>
using namespace std;
typedef void(*vp)(int &,int &);//将vp声明为一个函数指针类型,该类型的指针指向一个带有两个float型引用参数并返回void的函数
void square(int &x,int &y)
{
   x=x*x;
   y=y*y;
}
void cube(int &x,int &y)
{
   x=x*x*x;
   y=y*y*y;
}
void Swap(int &x,int &y)
{
   int z;
   z=x;
   x=y;
   y=z;
}
void print(vp,int &,int &);			//print函数的声明部分,该函数有三个参数,一个vp类型的函数指针,两个int型引用。
int main()
{
   vp p;
   int a=2,b=3;
   char choice;
   bool quit=false;
   while(quit==false)
   {
      cout<<"(0)退出(1)平方(2)立方(3)交换参数:";
      cin>>choice;
      switch(choice)
      {
         case '0':quit=true;
         case '1':p=square;break; 	//输入,将函数名square的地址赋给p
         case '2':p=cube;break;  	//输入,将函数名cube的地址赋给p
         case '3':p=Swap;break;  	//输入,将函数名Swap的地址赋给p
         default:p=0;break;
      }
      if(quit==true)break;
      if(p==0)
      {
         cout<<"请输入0到3之间的数字\n";
         continue;
      }
   print(p,a,b);					//调用这个将函数指针作为参数的函数
   }
   return 0;
}
void print(vp p,int &x,int &y)	//print函数的定义部分,函数头声明了3个接收参数,第1个是vp类型的函数指针p,它指向的函数带有两个参数并返回一个void值,另外还有两个int型引用x和y
{
   cout<<"执行函数前\n";
   cout<<"x:"<<x<<"\t"<<"y:"<<y<<endl;
   p(x,y);
   cout<<"执行函数后\n";
   cout<<"x:"<<x<<"\t"<<"y:"<<y<<endl;
}


 

类的函数指针

 

成员函数指针的声明:

void (A::*p)(int,int);

该语句生命了一个属于类A的函数指针p,p所指向的函数是类A的成员函数,他有两个int型参数,并返回void值。

实现该指针时,要先保证调用成员函数的对象是类A的对象,然后由该对象调用指针指向的函数,就像调用成员函数一样。

#include <iostream>
using namespace std;
class human
{
	public:
		virtual  void run()=0;
	    virtual  void eat()=0;
};
class mother:public human
{
	void run(){cout<<"母亲跑百米要花二十秒\n";}
	void eat(){cout<<"母亲喜欢吃零食\n";}
};
class father:public human
{
	void run(){cout<<"父亲跑百米要花十秒\n";}
	void eat(){cout<<"父亲不喜欢吃零食\n";}
};
class uncle:public human
{
	void run(){cout<<"叔叔跑百米要花十一秒\n";}
	void eat(){cout<<"叔叔喜欢吃零食\n";}
};
int main()
{
	void (human::*pf)()=0;    //生命一个成员函数指针pf,该指针指向抽象类human的成员函数
	
	human*p=0;

	char choice1,choice2;
	bool quit=false;
	while(quit==false)
	{
		cout<<"(0)退出(1)母亲(2)父亲(3)叔叔:";
		cin>>choice1;
		switch(choice1)
		{
		case '0':quit=true;
			break;
		case '1':p=new mother;
			break;
		case '2':p=new father;
			break;
		case '3':p=new uncle;
			break;
		default:choice1='q';break;
		}

		if(quit)
		{
			break;
		}
		if(choice1=='q')
		{
			cout<<"请输入0到3之间的数字\n";continue;
		}

		cout<<"(1)跑步(2)进食\n";
		cin>>choice2;
		switch(choice2)
		{
		case '1':pf=&human::run;break;
		case '2':pf=&human::eat;break;
		default:break;
		}
		(p->*pf)();       //通过指针p来访问对象,通过*pf来访问该对象的成员函数
		delete p;
	}
	return 0;
}


 

 

成员函数指针数组

#include <iostream>
using namespace std;
class paper
{
   public:
   void read(){cout<<"纸上面的字可以读\n";}
   void write(){cout<<"纸可以用来写字\n";}
   void burn(){cout<<"纸可以用来点火\n";}
};
typedef void(paper::*p)(); //利用typedef声明一个成员函数指针类型p,该类型的指针指向paper类的成员函数,该函数不具返回值且没有参数
int main()
{
   p func[3]={&paper::read,&paper::write,&paper::burn};//用类型p来声明一个func成员函数指针数组,并将它的成员函数指针初始化为它们所指向的函数的内存地址。与普通数组元素一样,成员函数指针数组的每个成员函数指针也会拥有一个编号,该编号从0开始
   paper*pp=0;					//声明一个指向paper类的普通指针
   char choice[1];				//声明一个只保存一个字符的char型数组choice
   bool quit=false;				//声明一个布尔变量quit并将它的值赋为false
   while(quit==false)				//当quit的值为false时
   {
     cout<<"(0)退出(1)读(2)写(3)点火:";  	//输出选择菜单
     cin>>choice[0];   				//将用户的选择保存在字符数组choice中
     if(choice[0]>'3' || choice[0]<'0') //判断该字符是否在0到3之间,假如不是
     {
       cout<<"请输入从0~3之间的数字\n";	 //提示用户输入
     }
     //否则,假如输入的字符在0~3之间
     else if (choice[0]=='0')  	//再判断该字符是否等于'0'
     {
        quit=true;    				//等于的话将quit赋为true,那么while条件不成立,退出循环
      }
      else 
      {
         int n; 			  		//定义一个整型变量用来接收被转换为整型的字符串
         pp=new paper;			  	//新构造一个paper类对象,用pp来指向它
         n=atoi(choice);	//将choice转换为整型后再赋给n
         (pp->*func[n-1])();		// pp指针访问新对象的成员函数,由该对象调用func 指针数组中下标为n-1的指针指向的成员函数,这里要注意数组的下标,n是用户输入的选项值,由于数组元素从0开始,所以要n减1,后面的()表示函数无参数
         delete pp;				//删除pp指针指向的新对象
      }
   }
   return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值