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 
{ 
       这里面同定义普通类一样,不必再说明基类中的成员 
}; 
       关于这里的派生性质,有这样一张表可供参考:  |