类的特殊成员
来源:互联网 发布:淘宝首页素材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;}
- 类的特殊成员
- 类的特殊数据成员
- Python类的特殊成员
- 类的特殊成员方法
- 类中特殊成员变量的初始化
- as类成员变量的特殊用法
- C++类的特殊数据成员
- 学习C++ -> 类的特殊数据成员
- 类中特殊成员变量的初始化
- 类中特殊成员变量的初始化
- 类的特殊成员变量初始化
- C++类的特殊数据成员
- Python类的特殊成员5
- Python进阶-类的特殊成员
- Python类的特殊成员方法
- python入门(三十二):类的特殊成员
- 类的成员介绍(特殊成员函数)
- 特殊成员变量的初始化
- Android定位功能(二)
- 已删除的东西还可以找回来吗?
- 创建带有UTF-8 的声明的XMLDocument
- 为什么你总有借口?
- SQL高效分页
- 类的特殊成员
- Extjs学习 Ext.grid.GridPanel
- 试验三:MicaZ-TinyOS2.x平台下点对点通讯试验-----BlinktoRadio实验
- 2012-07-13
- DS18B20程序(CPU时钟为1MHZ(51)和12Mhz(XC836))
- swing
- linux shell 流程控制(条件if,循环【for,while】,选择【case】语句实例
- struts2中的Action-validation.xml国际化传参问题
- 自己定义一个outputFormat