第十六章 类特殊成员
前面章节我们已经学会了C++类,继承,封装,多态等特性。接下来我们将要学习C++类的特殊成员.学习本章的基础是要了解变量,指针等基本用法.
C++ 这门语言赋予程序员及其强大的功能 , 能够操作内存就是其一特性。这种操作系统级别的语言强大的背后却带来了其学习难度大,学习周期长等特性。易则易知,简则易从。如果我们付出了相应的时间来学习这门庞大的语言,那么终将会在某天得到收获,而这些收获却是其他语言所无法比拟的。 言归正传,我们先开始学习特殊成员之一:类的静态变量.
请看简单的一个类\
#include<iostream>
using namespace std;
// 简单的Book类 class Book
{
public:
int num;
// 成员变量 void set(
int n){num=n;}
void get()
const{cout<<num<<endl;}
};
int main()
{
Book b;
b.
set(
20);
b.
get();
system(
" pause ");
return 0;
}
![](https://images.cnblogs.com/cnblogs_com/xuting/clip_image001.gif)
我们对着代码分析,我们创建了2个Book对象b,b2.两个对象的num成员是不同的.设置一个对象不妨碍另一个对象.比如我们在编写游戏的时候创建一个角色—牧师.同时有20个人在玩牧师这个角色,每个人的装备,等级不同.所以每个对象的成员变量互不影响.这个效果在以上这个程序中体现出来呢.也许我们会遇见另外一个情况,就是游戏后台需要统计共有多少人在创建牧师这个角色.这种要求在数据库中很好体现。而我们现在有种更简单的办法….那就是使用静态变量。请看程序代码
#include<iostream>
using namespace std;
// 简单的Book类 class Book
{
public:
static int num;
// 静态成员变量 Book(){num++;}
void get()
const{cout<<num<<endl;}
// Book::num };
// 注意下行写法 int Book::num=
0;
int main()
{
Book b,b2;
b.
get();
b2.
get();
cout<<Book::num<<endl;
system(
" pause ");
return 0;
}
![](https://images.cnblogs.com/cnblogs_com/xuting/clip_image0222201.gif)
可以看见每个类对象和用类本身访问静态变量都是输出了相同的数字。
静态变量和普通变量的定义区别就是添加static关键字,另外静态变量必须在全部初始化。
最后我们是在使用静态变量时注意以下几点
1:静态变量时属于类本身,而不属于类对象.静态变量时属于共享对象
2:静态变态必须在全局进行初始化\
3:访问静态变量时可以不用对象名去访问,可以用类名+限定符::+静态变量名.当然用对象名也是可行的.
4:静态变量在没有创建类对象之前就已存在.
私有静态变量
上面创建的是私有静态变量,假如我们不想公开这个静态变量,我们可以设置它为私有.那么要访问它,只有使用共有成员函数来访问.请看代码
using namespace std;
// 简单的Book类 class Book
{
public:
Book(){num++;}
void get()
const{cout<<num<<endl;}
// Book::num private:
static int num;
// 私有静态变量 };
// 注意下行写法 int Book::num=
0;
int main()
{
Book b,b2;
b.
get();
b2.
get();
// cout<<Book::num<<endl;错误访问,注释起来 system(
" pause ");
return 0;
}
总结:访问私有静态变量的唯一方法就是使用类提供的共有成员函数.要想使用公有函数,那必须前提是创建该类的一个对象。
静态成员函数
静态成员函数和静态成员变量,唯一不同的是静态函数只能访问静态成员.原因是静态函数没有this指针
函数指针
通过前几章的学习我们知道数组名是指向数组第一个元素的指针.如 int a[3={1,2,3}
那么这里我们可以把数组名a看做一个指向int 类型的指针.如a[1](数组表示法),a++(指针表示法).同理,我们也可以把函数名看做指向函数的指针,那么通过该指针就可以调用函数。
我们先来看以下函数指针的定义:
void(*p)();
前面的void是函数类型(*p)代表声明的是函数指针,后面的()里是函数参数.
Int(*p)(int ):这里是声明函数返回int类型,有一个int类型的参数.
我们不可以省略()。如果省略()的话,请看下面两句话意思
1:int(*p)(int)
2:int *p(int)
第一种声明一个函数指针,而第二种变成声明一个函数,返回类型为指针类型。
我们用一个简单的实例演示一下函数指针的用法
#include<iostream>
using namespace std;
int(*p)(
int);
// 声明函数指针 int temp1(
int a)
{
return a+a;
}
int temp2(
int a)
{
return a*a;
}
int main()
{
int num;
cout<<
" 请输入需要调用的函数(数字1-2) "<<endl;
cin>>num;
switch(num)
{
case 1:
p=temp1;
break;
case 2:
p=temp2;
break;
default:
p=temp1;
break;
}
int a=p(
10);
// 函数指针调用 cout<<a<<endl;
system(
" pause ");
return 0;
}
这里请注意函数指针和函数练习起来的方法:函数指针名=函数名….注意:无论有没有参数都不允许添加参数.
使用函数指针时需要注意以下几点;
1:函数指针返回类型必须和所要调用的函数一致(包括参数)
2:函数指针不能返回一个空值.这是使用指针的大忌.
函数指针数组
函数指针数组和函数指针类似,我们在这里先看下一个简单的函数指针数组用法
#include<iostream>
using namespace std;
int(*p[
2])(
int);
// 声明函数指针 int temp1(
int a)
{
return a+a;
}
int temp2(
int a)
{
return a*a;
}
int main()
{
for(
int i=
0;i<
2;i++)
{
switch(i)
{
case 0:
p[i]=temp1;
break;
case 1:
p[i]=temp2;
break;
}
cout<<p[i](i)<<endl;
}
system(
" pause ");
return 0;
}
将函数指针做为函数参数
在别的语言中如c#可以把委托作为参数给函数调用。而在C++我们也可以把函数指针做为参数给函数调用.C#委托了没有什么限制,而C++函数指针唯一的限制就是不能返回空值.我们先看以个简单的例子
// 我们用typedef简化一下函数指针声明和定义 typedef
int(*vp)(
int a);
// 用typedef 简化 void Print(vp p,
int a)
{
int temp=p(a);
cout<<temp<<endl;
}
int temp1(
int num)
{
return num+num;
}
int temp2(
int num)
{
return num*num;
}
int main()
{
vp p;
// 定义函数指针变量 p p=temp1;
// 用p指向 temp1函数测试 Print(p,
20);
p=temp2;
// 用p指向temp2函数测试 Print(p,
20);
system(
" pause ");
return 0;
}
我们对着输出结果看,第一次用p指向temp1,执行了20+20,第二次指向temp2,执行了20*20.而我们调用是用相同的话Print(p,20);。这个程序可以看出来把函数指针作为参数调用成功。
我们目前已经对函数指针有了解了,接下来我们再看函数指针如果指向类成员函数就不那么复杂了。。
类的函数指针
#include<iostream>
using namespace std;
class People
{
public:
void run(){cout<<
" 奔跑 "<<endl;}
void look(){cout<<
" 看世界 "<<endl;}
};
void(People::*pf)();
// 定义People内部函数指针pf int main()
{
pf=&People::run;
// 用pf绑定run方法 People p;
(p.*pf)();
system(
" pause ");
return 0;
}
可以看出定义类函数指针和普通函数指针唯一区别是加了类成员限定.
好了,到这里本章就告一段落.最后运用上面所有知识编写了简单的小游戏.
当然,这一些章节只是讲解C++,而MFC已经超出了章节范畴.所以我们的小游戏是在DOX运行。请看最后游戏输出
![](https://images.cnblogs.com/cnblogs_com/xuting/222.GIF)
代码如下
#include<iostream>
#include<
string>
using namespace std;
// 一个简单的狙击手类 class Sniper
{
public:
static int countNum;
// 定义总狙击手数量 Sniper();
~Sniper();
Sniper(
int _age,
string _name);
void run(
int num);
// 奔跑 void look(
int _no);
// 查看有没有发现敌人 void send(
int num);
// 发现敌人就打死跌人 int get(){
return no;}
private:
int age;
string name;
int no;
// 奔跑次数 bool isLook;
// 是否发现敌人; };
int Sniper::countNum=
0;
Sniper::Sniper()
{
age=
24;
name=
" 默认狙击手 ";
countNum++;
no=
0;
isLook=
false;
}
Sniper::Sniper(
int _age,
string _name):age(_age),name(_name)
{
no=
0;
isLook=
false;
countNum++;
}
void Sniper::run(
int num)
{
no++;
cout<<name<<
" 奔跑了 "<<num<<
" 米 ";
}
void Sniper::look(
int _no)
{
if(_no==
3)
{
cout<<
" 发现敌人 "<<endl;
isLook=
true;
}
else {
cout<<
" 没有发现敌人 "<<endl;
}
}
void Sniper::send(
int num)
{
if(isLook)
{
cout<<name<<
" 打出了 "<<num<<
" 发子弹将敌人消灭 "<<endl;
}
else {
cout<<name<<
" 说:没有发现敌人,不能浪费子弹 "<<endl;
}
}
// Sniper类声明和定义结束 void(Sniper::*sp)(
int num);
// 定义函数指针调用sniper类方法 int main()
{
// 程序刚开始运行我们向前奔跑 Sniper *sniper=
new Sniper(
24,
" AK47 ");
// 在椎中创建狙击手对象 for(
int i=
1;i<
4;i++)
{
sp=&Sniper::run;
(sniper->*sp)(
50);
sp=&Sniper::look;
(sniper->*sp)(sniper->
get());
sp=&Sniper::send;
(sniper->*sp)(
1000);
}
system(
" pause ");
return 0;
}
当前这个程序运行成员函数指针有点累赘.不过代码确实能很实际的描写出成员函数指针在C++里的运用。
本章节到这里结束