类的特殊成员

来源:互联网 发布:淘宝首页素材psd 编辑:程序博客网 时间:2024/05/17 07:08

静态变量

在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和yvoid cube(float &x,float &y){x=x*x*x;y=y*y*y;}   //该函数将参数x和y的立方再次赋给x和yvoid 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;}


 

原创粉丝点击