静态变量
在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;
}