2.3 类的静态成员>>
类的静态数据成员和普通的静态变量含义不同,它的意思是:在每一个类实例化时并不分配存储空间,而是该类的每个对象共享一个存储空间,并且该类的所有对象都可以直接访问该存储空间。其实它就是一个专门供这个类的对象使用的变量----如果你把它声明为private权限的话。>>
在类中定义静态数据成员,只须在定义时在前面加上"static"。类的静态数据成员只能在类外进行初始化,若没有对其进行初始化,则自动被初始化为 0。在类外引用静态数据成员必须始终用类名::变量名的形式。静态数据成员可以用来统计创建了多少个这种对象。>>
举一个例子:>>
#include <iostream>>>
using namespace std;
class AA
{
private:
int a;
public:
static int count; //定义类的静态成员
AA(int aa=0) { a=aa; count++; }
//对于类的方法,如果是较简单的可以这样写以使程序紧凑
int get_a( ) { return a; }
};
int AA::count=0; //在类外初始化
void main()
{
cout<<"Count="<<AA::count<<endl;
AA x(10),y(20);
cout <<"x.a="<<x.get_a( )<<" Count="<<x.count<<endl;
cout <<"y.a="<<y.get_a( );
}
我们还可以定义静态的函数,只需在声明函数时在前面加上static即可,但定义函数时就不用加上static了。使用静态函数时也需要"类名::静态成员函数名(参数表);"的形式。
运算符重载可以使类变得非常直观和易用。比如说,我们定义了一个复数类(什么,你没学过复数?你读几年级?),然后再将加、减、乘、除等运算符重载,就可以自由地对复数对象好像整数一样进行这些运算了!可以想象,这将大大方便我们。
使用运算符重载很简单,我们就举一个复数类的例子来说明怎样使用:
#include <iostream>
using namespace std;
class complex
{
private:
double real;
double image;
public:
complex ( ); //缺省构造函数
complex (double r, double i); //顺便初始化值的构造函数
complex operator +(complex x); //计算A+B
complex operator ++( ); //计算++A
complex operator --(int); //计算A—
complex operator =(double x); //把一个double赋给一个complex时该怎么办
//系统还自动生成了一个complex operator=(complex);,它的实现是简单拷贝
//所以如果类中有指针成员,它会像默认的拷贝构造函数那样出问题L
//我们如果要重写它,还要注意检查自己赋给自己的情况
void print(); //输出复数
};
complex::complex( )
{
real=0.0f;
image=0.0f;
}
complex::complex(double r, double i)
{
real=r;
image=i;
}
complex complex: perator +(complex x)
{
complex c;
c.real=real+x.real;
c.image=image+x.image;
return c;
}
complex complex: perator ++( )
{
complex c;
++real;
c.real=real;
c.image=image;
return c;
}
complex complex: perator --(int)
{
complex c;
c.real=real;
c.image=image;
real--;
return c;
}
complex complex: perator =(double x)
{
real=x;
return *this; //按照C++的惯例,返回*this,以便实现链式表达式
}
void complex::print( )
{
cout<<real<<"+"<<image<<"i"<<endl;
}
void main( )
{
complex a(1,2);
complex b(4,5);
complex c=a+b;
complex d=++a;
complex e=b--;
//complex f=0.234; //这样写现在还不行,因为上面没写相应的拷贝构造函数
//你可以试着写一个
complex f;
f=a=0.234; //链式表达式
a.print( );
c.print( );
d.print( );
e.print( );
f.print( );
}
除了"."、 ".*"、 "::"、 "?:"四个运算符外,其它运算符(包括new、delete)都可被重载,cin和cout就是两个典型的例子。
对于双目运算符(即A?B),如加、减、乘、除等,可这样重载:
"complex operator ?(complex B);",运算时就好像调用A的这个方法一样。
对于前置的单目运算符(即?A),如"-A"、 "--A"、"++A"等,可这样重载:
"complex complex: perator ?( );"。
对于后置的单目运算符,如"A--"、 "A++",可这样重载:
"complex complex: perator ?(int);",其中参数表中的int不能省去。
下面出一道题让大家考虑考虑吧:创建一个字符串类并将+、-、=、==等运算符重载,使我们可以直观地操作字符串。
OCK aspectratio="t" v:ext="edit">OCK>
可以继承是类的第二个优点,它使大型程序的结构变得严谨并减少了程序员的重复劳动。继承到底是什么呢?举个例子,比如说树和猫这两样东西,看起来好像毫不相干,但它们都有质量、体积等共有的属性和买卖、称量等共有的方法。所以我们可不可以先定义一个基类,它只包含两样事物共有的属性和方法,然后再从它派生出树和猫这两样事物,使它们继承基类的所有性质,以避免重复的定义呢?答案是肯定的。由于一个类可以同时继承多个类,一个类也可同时被多个类继承,我们可以建立起一个复杂的继承关系,就象这样:

图2.1
我们可以看到,继承是很灵活样的。要说明继承是很简单的,只要像这样定义派生类即可:
class 派生类名 :派生性质 基类名1,派生性质 基类名2,...,派生性质 基类名n
{
这里面同定义普通类一样,不必再说明基类中的成员
};
关于这里的派生性质,有这样一张表可供参考: |