1.问题:要求写一个分数类,有分子分母,能设置分子分母,能够约分,能显示小数。
思路:与上一章例程不同的是,在约分的过程中,先求最大公约数,然后得到约分后的分子和分母。
代码:#include
using namespace std;
class Fract{
int n;
int d;
public:
void show(){
cout << n << '/' << d << endl;
}
int maxdiv(int a, int b){//
cout << n << '/' << d <<',';//用于观察最大公约数的计算过程
return a==0||b==0?a+b:maxdiv(b,a%b);
}
void reduce(){
int div = maxdiv(n, d);
if(div!=0){
n /= div;
d /= div;
}
}
Fract(int n, int d=1)//构造函数
:n(n),d(d)
{
cout << n << '/' << d << endl;//用于观察构造函数对数据成员初始化过程
reduce();
}
Fract():n(0),d(1){}//构造函数
};
int main()
{
Fract (3,6).show();
Fract (3,4).show();
Fract (3).show();
Fract ().show();
Fract f1;//如果没有参数可传,就相当于函数声明。创建对象时,如果没有参数可传递,不要加“()”
}
解释:Fract (3,6)=>Fract(int n=3, int d=6)=>:n(3),d(6)=> maxdiv(3, 6)=>maxdiv(6, 3%6=3)=>maxdiv(3, 6%3=0)
=>a+b=3=>div=3=>n=3/3=1,d=6/3=2=>1/2
2.问题:临时空间,即构造函数与对应析构函数配对执行。请编例程。
思路:临时空间只能一次性使用,在那个语句后就立即释放
代码:#include
using namespace std;
class A{
int d;
public:
A(int d=0):d(d){cout<
int main()
{
A a1;//直到碰到主函数‘}’,才有可能运行析构函数。如果多个构造函数,并且是按照一定顺序执行析构函数。
A(5);// 可以看成强制类型转换:调用一次构造函数,就完成任务。
A a3(3); //直到碰到主函数‘}’,才有可能运行析构函数。
cout<<"-------------"<
解释:多个析构函数要执行时,遵守栈原理,即先进后出,后进先出的顺序。
3.问题:输出一个对象(名和地址)“结婚与”另一对象(名和地址);声明用已有对象初始化的对象;创造形参为对象,返回值也是对象的
函数。
思路:1.定义Person类:
1.1.定义数据成员:声明name,Person类指针对象lover
1.2.定义函数成员:1.构造函数及其类数据成员初始化,输出name及其地址
2.定义析构函数,输出name及其地址
3.定义marry函数:
3.1.形参为对象(这样,如果实参传到,就会再次调用构造函数,而这是非基本类型构造函数;等到marry函数执行完,就调用对应析构函数)
3.2.把收到实参对象重新初始化后地址存到调用对象的lover指针变量中
3.3.把当前地址传给实参对象的lover指针变量,输出调用对象的name和地址,传送实参对象name和地址(首数据成员地址与对象地址相同)
4.拷贝构造函数:1.即非基本类型构造函数,比如对象。其形参为引用,即把传送实参对象的地址传给形参对象(即实参地址==形参地址,如
果相关数据成员内容发生变化,两种调用同一个变化内容)(将会重新调用对应传送实参对象)
2.输出形参对于name和及其地址(即重新调用的实参对象地址)
3.把形参对象name前加“克隆”给实参name。(执行多少次非基本类型构造函数,加几个“克隆”给实参对象name)
4.初始化lover(因为拷贝构造函数也是构造函数嘛)
2.定义返回值也是对象的函数echo,形参也是对象。(即本函数在始末将各自调用一次构造函数,结束时会调用一次形参对象对应析构函数)
3.1.声明cgx对象,初始化为“陈冠希”
3.2.声明fr对象,初始化为“芙蓉”
3.3.cgx对象,调用person类marry函数成员,传送给形参,fr对象
3.4.声明fr2对象,初始化为fr对象(调用对象fr2的拷贝构造函数,在本程序中未再使用fr2对象,所以在主函数结束实调用其析构函数)
3.5.调用echo函数(因为它将调用两次拷贝构造函数,将会在echo函数结束时,调用析构函数依次执行。同一类的不同对象的成员调用会存放
在栈中。echo函数不是对象的函数成员,所以不在栈中,不遵守栈的顺序。)
4.自动依次调用剩下的对应析构函数
代码:#include
using namespace std;
#include
class Person{
string name;
Person* lover;//这个数据成员其实没用上其值,虽然是使用了其变量。
public:
Person(string n):name(n),lover(NULL){
cout<
~Person(){
cout<
void marry(Person one){
lover=&one;
one.lover=this;
cout<
Person(const Person& p){// 在向函数传递非基本类型的数据时候,尽量使用引用,如果不需要修改就加const 。// 也叫拷贝构造函数
cout<<" 克隆"<< p.name <<",ID:"<
lover=NULL;
}
};
Person echo(Person p)// 注意了return 后的也是一个变量//可选代码,即函数echo
{
return p;// 注意了return 后的也是一个变量
}
int main()
{
Person cgx(" 陈冠希");
Person fr(" 芙蓉");
cout<<"---------------------"<
Person fr2(fr);
cout<<"---------------------"<
cout<<"---------------------"<
补充:如果不选可选代码,结果将是如下。
陈冠希出生了,身份证号:0xbfd54ca0
芙蓉出生了,身份证号:0xbfd54c98
---------------------
克隆芙蓉,ID:0xbfd54cb8
陈冠希(ID:0xbfd54ca0) 跟克隆芙蓉(ID:0xbfd54cb8) 相爱了!
克隆芙蓉千古了,身份证号:0xbfd54cb8
克隆芙蓉,ID:0xbfd54c90
---------------------
克隆芙蓉千古了,身份证号:0xbfd54c90
芙蓉千古了,身份证号:0xbfd54c98
陈冠希千古了,身份证号:0xbfd54ca0
解释:1.拷贝构造函数,注意形参传递的过程:如果自己没写,系统会自动产生一个拷贝构造函数,它会把形参对象的数据成员。
逐个复制到指定对象中,但只复制数据成员。形参是独立与实参的一种变量,并不能认为形参就是实参。
2.可选代码部分:返回值临时变量也是变量,是用return 后的数据初始化的返回的值也是一个临时对象。
3.可选代码部分:为什么会产生“克隆克隆 芙蓉:ID:...”?形参会产生一个“克隆 芙蓉”,在echo函数结束前,还要执行一次拷贝构造函
数。因为echo函数未结束,没有对形参的“克隆 芙蓉”析构,还保留在fr.name地址中。而返回的还是原形参对象,再次执行拷贝构造函数时
,仍然会保存在fr.name地址中,所以就那啥了。然后才会结束echo函数,接着运行析构函数。
4.问题:编写一个时钟程序,指定时间开始计时,到指定时间输出“时间到”,继续开始计时
思路:重点介绍继承部分代码的执行过程:
定义Alarm类:class Alarm :public Clock,这段代码表示定义继承关系的确立。然后执行构造函数,:atime(ah,am,as),Clock(h,m,s),表示
把接收到的对应实参调用构造函数Time(int h,int m,int s):h(h),m(m),s(s);另外对应实参则直接调用Clock类构造函数Clock(int h,int
m,int s):now(h,m,s)(他们是父子关系,不用通过对象间接调用),间接拥有了now对象。
代码:#include
using namespace std;
#include
class Time{
int h;
int m;
int s;
public:
Time(int h,int m,int s):h(h),m(m),s(s){}
void advance(){
if(++s>=60){
s=0;
if(++m>=60){
m=0;
if(++h>=24)
h=0;
}
}
}
void nextsecond(){
long start=time(NULL);
while(time(NULL)==start);
}
void show(){
cout<<'/r';
if(h<10) cout<<0;
cout<
cout<
cout<}
bool equal(const Time& t){//Time类中增加的代码//引用,执行时,t地址==atime地址
return (h==t.h&&m==t.m&&s==t.s);
}
};
class Clock{
protected:// 对于继承它的类是开放的,对于其它则不然
Time now;
public:
void run(){
for(;;){
now.nextsecond();
now.advance();
now.show();
}
}
Clock(int h,int m,int s):now(h,m,s){}
};
class Alarm :public Clock{// 继承不是拷贝//增加了这部分内容,比上一章15题。
Time atime;
public:
Alarm( int h,int m,int s,int ah,int am,int as
):atime(ah,am,as),Clock(h,m,s){}// 只能在初始化类表传
void run(){
for(;;){
now.nextsecond();//同理,now是Time类,所以可以调用Time类中成员函数
now.advance();//同理,now是Time类,所以可以调用Time类中成员函数
now.show();//同理,now是Time类,所以可以调用Time类中成员函数
if(now.equal(atime))//同理,now对象调用Time类中equal成员函数:判断now对象时间==atime对象时间?true:false;
cout<<"/n 时间到/n"<
}
};
int main()
{
Alarm a(23,59,50,23,59,55);//重点介绍这个类的构造函数执行过程
a.run();
}
解释:1.形参和实参不是同一个
2.继承:一个类中拥有另一个类的内容
3.Alarm类继承了Clock类,而Clock类有数据成员是Time类对象,同时Alarm类有数据成员也是Time类对象。所以从Alarm类角度看,既可以使用
Time类的成员,也可以用Clock类成员。 类成员函数,而不执行“停车”所在函数体,即相当于空函数体 束时,依次执行父子类析构函数 创建动态数据空间,调用创建类函数成员,释放空间->调用析构函数 们;输出结果 不是本类成员的函数 类成员,也能用父类成员,顺便看一下)->对象用自身函数成员,实参为父类对象及对于数据成员值,通过引用存到指定对象的指定数据成员 中->输出父类对象数据成员->对象自身函数成员,实参为两个父类对象(调用指定对象指定数据成员值)->对象自身函数成员,实参为父类对象 和参加运算参数->输出父类对象数据成员->同理计算输出距离
5.问题:有无实参子类初始化过程演示
思路:无
代码:#include
using namespace std;
class Parent{
public:
Parent(int d){
cout<<"Parent(int)"<
Parent(){
cout<<"Parent()"<
};
class Child:public Parent{
public:
Child(int d):Parent(d){//直接调用父类对应构造函数
cout<<"Child(int)"<
Child(){
cout<<"Child()"<
};
int main()
{
Child c1;
cout<<"----------"<
}
解释:从子类中改写来自父类的函数称为覆盖或隐藏
6.问题:定义父子类,子类对象执行子类函数成员时,子类对象直接调用父类函数成员
思路:声明带实参对象->对象执行父函数成员->对象执行子函数成员->执行析构函数
代码:#include
using namespace std;
#include
class Person{
string name;
bool gender;
int age;
public:
Person(string n,bool g):name(n),gender(g),age(0){
cout<<" 做人的准备工作"<
void grow(int n){
age+=n;
cout<
void show(){
cout<<" 我是"<<(gender?" 帅哥":"美女")<
string getName(){return name;}
bool getGender(){return gender;}
int getAge(){return age;}
~Person(){
cout<<" 做人的退休工作"<
};
class Teacher:public Person{
string course;
public:
Teacher(string n,bool g):Person(n,g){
cout<<" 做老师的准备工作"<
~Teacher(){
cout<<" 做老师的退休工作"<
void setCourse(string c){
course=c;
}
void teach(string classno){
cout<
void show(){
cout<<" 我是学校"<
};
int main()
{
Teacher zr(" 赵蓉",false);//在执行子类构造函数中,先执行父类构造函数,用子类实参初始化父类的数据成员
//(等于间接成为父类对象),执行完子类构造函数
zr.grow(32);//直接执行父类函数成员
zr.setCourse("Oracle");//执行子类函数成员,实参保存在course变量中
zr.teach("0805");//执行父类函数成员获得,父类数据成员值name;
//实参获得classno;从course变量获得course
zr.show();//与上一行同理,获得对应值
}//父子类的析构按照栈的顺序执行
解释:1.一个子类的对象也是父类的对象,前提是要在执行子类构造函数时要把子类实参赋值给父类数据成员,进行初始化
2.当class A{public:int x}
class B:public A{}; 时,
父类 子类 其它地方
直接用x 直接用x A a;B b;a.x;b.x; 不能直接用x
7.问题:用例程表示虚函数和多态的概念
思路:1.定义父类Vehicle,子类Car,Bike,Horse,相对独立类Lights
2.声明Car对象,Bike对象,Lights对象。Linght对象运行其函数成员,分两次传送Car对象,Bike对象
3.声明Horse类,Linght对象运行其函数成员,Horse对象
代码:#include
using namespace std;
#include
class Vehicle{
string type;
public:
Vehicle(string t):type(t){cout<<"交通工具"<
virtual void stop(){cout<
};
class Car:public Vehicle{
public:
void stop(){cout<<" 汽车司机两脚分别踩离合和刹车"<
class Bike:public Vehicle{
public:
void stop(){cout<<" 权哥两手使劲捏车闸"<
class Lights{
public:
void stopVehicle(Vehicle& v){//注意这是对Vehicle类的引用,即是对父类的引用
v.stop();
}
};
class Horse:public Vehicle{
public:
void stop(){cout<<" 骑士提缰绳吆喝"<
int main()
{
Car c(" 小汽车");
Bike my26("26 车");
Lights rg;
rg.stopVehicle(c);
rg.stopVehicle(my26);
Horse h;
rg.stopVehicle(h);
}
解释:1.多态:统一管理虚函数。
如果在调用父类函数时需要执行对象所属的子类中的函数,就需要在父类中把这个函数声明成虚函数 关键字:virtual
好处:无论以后增加多少子类都不用声明。多态的可拓展性非常强,可以无限扩展,统一管理。
2.一个子类实参,传送到其父类形参之中,先调用父类构造函数,再调用子类构造函数,然后执行函数体(有其父,才有其子)。此函数体结
3.此时,virtual void stop(){cout<
8.问题:虚函数占用空间是否与其他空函数体函数占用空间相同?
思路:无
代码:#include
using namespace std;
class A{
int data;
char c;
public:
void f(){}
};
class B{
int data;
char c;
public:
virtual void f(){}
};
int main()
{
cout<<"sizeof(A)="<
解释:有虚函数的可能会有空闲字节
9.问题:程序表达运用指针,引用和动态空间,调用子类函数成员的过程
思路:声明子类对象->声明父类指针,用子类地址初始化,并调用子类函数成员->声明父类引用,用子类地址初始化,并调用子类函数成员->
代码:#include
using namespace std;
class Base{
public:
void g(){cout<<"Bg"<
class Derived:public Base{
public:
void g(){cout<<"Dg"<
int main()
{
Derived d;
Base* p=&d; //指针变量p中内容是对象地址,即父类对象地址
Base& r=d;//r地址==d地址
p->f();// (*p).f();
r.f();//等于d.f();
cout<<"=================="<
r.g();
cout<<"=================="<
p->f();// 观察与上一个程序的区别
delete p;//释放空间,之后就不能调用p了。如果没有virtual,参考解释1.;有,调用子类析构,然调后父类析构,也是多态???
cout<<"=================="<
解释:1.只有在用基类(==父类)指针指向new的派生类(==子类)对象时delete才可能只调基类析构不调派生类析构
2.组合:一个对象“有”几个成员对象
继承:‘是’,而组合:‘有’;
多态:统一以父类指针或者引用来管理各种子类对象
(1)前提:继承关系(2)调用:虚函数(3)??引用或指针
3.虚函数的使用:(1是规定 2和3是建议)
1、构造函数不能是虚函数
2、如果类中有任何一个成员函数是虚函数,那么析构函数应为虚函数
3、如果一个类肯定被用作其他派生类的基类,尽可能使用虚函数
10.问题:按照多重继承;同名解析函数;解除同名解析,转化成菱形继承,实现同样功能;这3步思路逐步表示菱形继承
思路1:定义类Phone,MP3,MusicPhone->声明对象mp并初始化,各自调用父类函数成员->执行析构函数
思路2:按照栈的顺序构造和析构及同名解析
思路3:抽象出Product类,形成菱形继承结构
代码1:#include
using namespace std;
#include
class Phone{
string mark;
public:
Phone(string m):mark(m){
cout<<" 购"<
void call(string name){
cout<<" 用"<}
~Phone(){
cout<<" 手机报废"<
};
class MP3{
public:
MP3(int n){
cout<
void play(string song){
cout<<" 播放歌曲"<
~MP3(){
cout<<" MP3报废"<
};
class MusicPhone:public Phone,public MP3{//多重继承
public:
MusicPhone(string m):Phone(m),MP3(4){
cout<<"......."<
};
int main()
{
MusicPhone mp(" 夏新");
mp.call(" 芙蓉");
mp.play(" 你是我的玫瑰花");
}
代码2:
#include
using namespace std;
#include
class Phone{
double price;
string mark;
public:
Phone(string m, double p):mark(m),price(p){//修改部分
cout << " 购" << m << " 手机" << endl;
}
void call(string name){
cout << " 用" << mark << " 手机给" << name << " 拨电话" << endl;
}
double getPrice(){return price;} //添加部分
~Phone(){
cout << " 手机报废" << endl;
}
};
class MP3{
double price;
public:
MP3(int n, double p):price(p){ //修改部分
cout << n << "G 的MP3" << endl;
}
void play(string song){
cout << " 播放歌曲" << song << endl;
}
double getPrice(){return price;} //添加部分
~MP3(){
cout<<" MP3报废"<
};
class MusicPhone: public Phone, public MP3{
public:
MusicPhone(string m, double p):Phone(m,p),MP3(4,p){
cout << "...." << endl;
}
};
int main()
{
MusicPhone mp(" 夏新", 1234.5);
mp.call(" 芙蓉");
mp.play(" 你是我的玫瑰花");
cout<
代码3:#include
using namespace std;
#include
class Product{//增加部分
double price;
public:
Product(double p):price(p){
cout << "price " << p << endl;
}
double getPrice(){return price;}//想得到类中私有数据成员的值,只能通过调用公开函数成员来实现。
};
class Phone : virtual public Product{//修改部分
string mark;
public:
Phone(string m, double p):mark(m),Product(p){
cout << " 购" << m << " 手机" << endl;
}
void call(string name){
cout << " 用" << mark << " 手机给" << name << " 拨电话" << endl;
}
~Phone(){
cout << " 手机报废" << endl;
}
};
class MP3: virtual public Product{//修改部分
public:
MP3(int n, double p):Product(p){
cout << n << "G 的MP3" << endl;
}
void play(string song){
cout << " 播放歌曲" << song << endl;
}
~MP3(){
cout<<" MP3报废"<
};
class MusicPhone: public Phone, public MP3{
public:
MusicPhone(string m, double p):Phone(m,p),MP3(4,p),Product(p){
cout << "...." << endl;
}
};
int main()
{
MusicPhone mp(" 夏新", 1234.5);
mp.call(" 芙蓉");
mp.play(" 你是我的玫瑰花");
cout << mp.getPrice() << endl; //不同部分
}
解释:虚继承:表示来自父类的成员允许合并
虚基类 菱形继承 虚基类的构造函数由底层子类直接传递
11.问题:How把一个常量转换成变量
思路:无
代码:#include
using namespace std;
int main()
{
int n=100;
const int m=n;//这时的m是常量,但是不是固定值
cout<<"m="<
cout<<"----------"<
cout<<"m="<
const_cast
cout<<"c="<
cout<<"c="<<*p<
解释:类型转换的格式:xxx—cast< 类型> ( 数据)
类型转换 (类型)数据 / 类型(数据) / (类型)(数据)
类型转换 算子 尽量不用类型转换
static—cast< 类型>(数据) 用于合理的类型转换
(1) 数值类型之间(2) 具体指针与void* 之间(3) 父子类之间及其
指针间(4) 无名/临时对象形式的
不合理的:const—cast
12.问题:父子类动态类型转换例程
思路:定义父类指针动态空间,一个指向父类空间,一个指向子类空间;定义两个子类指针,把父类和子类指针转换类型,使子类指针指向它
代码:#include
using namespace std;
class Person{
public:virtual void work(){}
};
class Driver:public Person{
public:
virtual void work(){}// 开车
};
int main()
{
Person* p1=new Person;
Person* p2=new Driver;
Driver* d1=dynamic_cast
Driver* d2=dynamic_cast
cout<<"d1="<
解释:1.dynamic_cast<子类*>(父类指针)
从父类指针到子类指针的尝试转换,如果是子类类型转换成功将有其地址,不成功就会转换成NULL,也就是空指针,如d1=0。
多态中要求父类中必须有虚函数
尽量只使用多态,而不是作类型转换和类型识别
2.reinterpret_cast<>()
1/任意两种指针之间
2/指针与足够宽度的数值类型之间
避免用它
13.问题:用1/2+1/3=5/6,解释友元授权
思路:友元授权(友元,即特殊关系户,当然受到与父子关系同等待遇,汗颜!)
代码:#include
using namespace std;
class F{
int n;
int d;
public:
F(int n,int d):n(n),d(d){}
friend F add(const F& f1,const F& f2);// 形参的名字可以不写// 授权函数,功效等同于继承
friend ostream& output(ostream& os,const F& f);// 授权函数,功效等同于继承
};
F add(const F& f1,const F& f2)// 养成带const 的习惯
{
int d=f1.d*f2.d;// 必须是对象.fd
int n=f1.n*f2.d+f1.d*f2.n;
return F(n,d);
}
ostream& output(ostream& os,const F& f)//os 相当于cout
{//将void 该成了ostream ( 注意)
os<
}
int main()
{
F f1(1,2),f2(1,3);
output(cout,f1)<<"+";
output(cout,f2)<<"=";
output(cout,add(f1,f2))<
解释:友元关键字:friend 不是成员。主要用在运算法重载中,类对某函数授权,允许直接访问本类对象中的任何成员,必须在本类内部授权
14.问题:友元类例程
思路:简直就是结义金兰!想用什么用什么!
1.看主函数:声明三个父类对象(想:就联想到父类的成员:数据和函数,那就顺便看一下)->声明友元类对象(想:这个对象即可以用自身
2.(原来还想进一步细致深入的研究一下深层运行,看主函数,捎带整个程序运行完毕)
代码:#include
using namespace std;
#include
class Point{
double x;
double y;
friend class Util;// 把整个类都设为友元
};
class Util{
public:
void move(Point& p,double x,double y){//关系真铁,太霸道了
p.x+=x;
p.y+=y;
}
double distance(const Point& p1,const Point& p2){
double x=p1.x-p2.x;
double y=p1.y-p2.y;
return sqrt(x*x+y*y);
}
void set(Point& p,double x,double y){//关系真铁,太霸道了//引用竟然可以这样用?原来是对象的引用啊!
//为什么这样赋值?不用构造函数?尚未比较清楚,且看以后2遍更新!
p.x=x;
p.y=y;
}
void print(const Point& p){
cout<<'('<
};
int main()
{
Point p1,p2,p3;
Util u;
u.set(p1,0,0);
u.set(p2,3,0);
u.set(p3,3,4);
u.print(p1);
u.print(p3);
cout<
u.print(p2);
cout<
解释:得到结果不重要,重在整个程序执行过程,多在脑中转几遍,过程中的几个概念要记住。
15.问题:只读函数成员例程
思路:无
代码:#include
using namespace std;
#include
class Course{
string name;
int days;
public:
Course(string n,int d):name(n),days(d){}
void show()const{//const 表示本函数不会修改当前对象
cout<
void show(){// 注意没有const//若注释掉本函数,将得不同结果
cout<
void extend(int d){
days+=d;
}
};
int main()
{
Course cpp("C++",19);
Course uni("UNIX",3);// 不能用unix
cpp.extend(1);
cpp.show();
uni.show();
}
解释:静态和只读成员,只读函数(常量函数)。有const函数,肯定不改;无const函数,有可能改。
16.问题:初始化数组对象,定义初始化调用静态数据成员。给出例程
思路:声明数组对象(包括初始化),循环调用对象函数成员(执行时会调用静态数据成员值)
代码:#include
using namespace std;
#include
class Student{
string name;
bool gender;
public:
static int room;//静态数据成员
static string teacher;//静态数据成员
Student(string n, bool g=true):name(n),gender(g){
cout << (gender?" 帅哥":"美女") << name << " 报名了" << endl;
}
void study(){
cout << name << " 在" << room << " 听" << teacher << " 讲课" << endl;
}
static void changeRoom(int r){//静态函数成员,未用
room = r;
}
static void changeTeacher(string t){//静态函数成员
teacher = t;
}
};
int Student::room = 411;//静态数据成员的初始化
string Student::teacher = " 赵蓉";//静态数据成员的初始化
int main()
{
Student s[4]={
Student(" 杨智"),
Student(" 史闻军"),
Student(" 廖英旭",false),
Student(" 周桐"),
};
for(int i=0; i<4; i++)
s[i].study();
Student::changeTeacher(" 大哥");//改变静态数据成员的值
for(int i=0; i<4; i++)
s[i].study();
}
解释:不依赖于对象只依赖于类,静态数据成员的初始化要在外面进行。在静态函数成员中没有当前对象,只能访问不依赖于对象的成员,也
就是静态成员。