您的位置:首页 > 编程语言 > C语言/C++

C++基础--静态成员

2008-09-02 14:46 190 查看
C++基础--静态成员(1)
 前面我们已经看到:每一个类对象有其公有或私有的数据成员,每一个public或private函数可以访问其数据成员。
有时,可能需要一个或多个公共的数据成员,能够被类的所有对象共享。在C++中,我们可以定义静态(static)的数据成员和成员函数。
10.6.1 静态数据成员
  要定义静态数据成员,只要在数据成员的定义前增加static关键字。静态数据成员不同于非静态的数据成员,一个类的静态数据成员仅创建和初始化一次,且在程序开始执行的时候创建,然后被该类的所有对象共享;而非静态的数据成员则随着对象的创建而多次创建和初始化。

下面是静态数据成员定义的实例:


例10-18
class Test
{
 public:
  static int public_int;
 private:
  static int private_int;
};

void main()
{
 Test::public_int = 145; // 正确
 Test::private_int = 12; // 错误,不能访问私有的数据成员
}

从上例我们可以看到:静态数据成员的访问方式是:
  类名::静态数据成员名
但是,不能直接访问私有的数据成员。其实,上面的程序段只是为了介绍如何访问静态数据成员,不能通过编译器的的编译和连接。

一、私有的静态数据成员
  为了说明私有的静态数据成员的使用,考虑下面的程序段:
  class Directory
  {
   public:
   ...
   private:
    // 数据成员
    static char path[];
  };
  数据成员path[]是一个私有的静态变量,在程序执行过程中,仅一个Directory::path[]存在,即使有多个Directory类的对象。静态的数据成员能够被类的成员函数访问,但不能在构造函数中初始化。这是因为静态数据成员在构造函数被调用之前就已经存在了。静态数据成员可以在定义时初始化,且必须在类和所有的成员函数之外,与全局变量初始化的方法一样。例如,类Directory的数据成员的定义与初始化方法如下:
  // 静态数据成员的定义与初始化
  char Directory::path [200] = "/usr/local";
  // 无参的构造函数
  Directory::Directory()
  {
   ...
  }
  在类中的静态数据成员的定义,只是说明该类有这么一个数据成员,并没有为该数据成员分配内存。就象非静态数据成员是在创建对象时分配内存一样,静态数据成员是在初始化时分配内存的。所以,在定义静态的数组成员时,可以省略它的尺寸(数组元素的个数),但在初始化时,必须确定数组的尺寸。

二、公有的静态数据成员
  数据成员也可以是公有的,不过我们很少定义公有的数据成员,因为它破坏了数据隐藏的原则。如果Directory类的成员path[]定义为公有的,则该成员可在类外的任一地方访问:
  void main()
  {
   strcpy(Directory::path, "/usr/local/pub"); //修改path的值
   …
  }
  path仍然必须先初始化:
  char Directory::path[200];
为什么需要静态数据成员?
  类的静态数据成员拥有一块单独的存储区,而不管我们创建了多少个该类的对象。也就是说,静态数据成员被类的所有对象共享,它是属于类,而不是属于对象的。所有对象的静态数据成员都共享一块静态存储空间,可以节省内存,也为对象之间提供了一种互相通信的方法。静态数据成员的作用域在类内,也有public(公有)、private(私有)或者protected(保护)三种访问权限。
静态数据成员的存储空间分配
  对于非静态数据成员而言,有多少个对象,就有多少个不同的内存单元,它们分布在各个对象的存储空间中。静态数据成员不同于非静态数据成员,不管有多少个对象,它们都共享相同的内存单元。所以,静态数据成员不应该属于任何一个对象,说它属于类更确切。
  我们用一个例子来说明这种存储空间的关系, 对于类:
  class C
  {
   static int si;
   static int sc;
   int i;
   char c;
   //……
  };
  如果我们创建了类C的三个对象c1、c2和c3,那么它们的数据成员的存储关系如图10-2所示:


图10-2


静态数据成员的初始化
  静态数据成员的初始化必须在类外,例如:
  class A
  {
   static int i;
   public:
   //……
  };
  静态数据成员i的初始化方法为:"int A::i=1;"。可见:它与全局变量的初始化的方法的不同之处在于,有类名和作用域分隔符指定i的范围。
  我们还可以定义静态的数组成员,例如:


例10-19
class Values
{
 static const int size;
 static const float table[4];
 static char letters[5];
};
const int Values::size = 100;
const float Values::table[4] = {1.1, 2.2, 3.3, 4.4};
char Values::letters[5] = {'a', 'b', 'c', 'd', 'e'};
void main(){}

  本例创建了静态的成员变量size,静态的数组成员table和letters。静态的数组成员的初始化方法与数组的初始化类似,但要有类名和作用域分隔符限制。
成员常量
  我们先看下面一个实例:
  class A
  {
   const size = 100; //illegal
   int array[size]; //illegal
  };
  类A是想定义一个常量数组,但这个类的定义是错误的。因为定义类常量数据成员时,不能同时进行初始化。定义类时,只是说明类有那些数据成员,而不涉及到内存单元的分配,类数据成员存储单元的分配是在对象初始化时进行的。
  我们也可以把常量数据成员定义成静态的,同静态成员变量初始化一样,静态成员常量初始化也在类外,例如:


例10-20
class A
{
 static const int size;
 int array[size];
 public:
 // ...
};
const int A::size = 100; //definition

嵌套类和局部类中静态成员变量的使用
  可以很容易地把一个静态数据成员放在一个嵌套类中,它是非嵌套类中静态数据成员情况的扩展,只将它的范围进行另一个级别的指定就可以了。然而,在局部类(如在函数内部定义的类)中不能有静态数据成员。例如:


例10-21
// Nested class CAN have static data members:
class outer
{
 class inner
 {
  static int i; //OK
 };
};
int outer::inner::i = 100;

// Local class CANNOT have static data members:
void f()
{
 class local
 {
  static int i; //error
 };
}

C++基础--静态成员(2)

2 静态成员函数
  除静态数据成员外,C++也允许定义static成员函数。静态的数据成员被所有的对象共享,也就是说,静态数据成员不属于对象,而是属于类的。与静态数据成员类似,静态成员函数也是属于类的。
  注意:静态成员函数仅能访问静态的数据成员,不能访问非静态的数据成员,也不能访问非静态的成员函数,这是由于静态的成员函数没有this指针。类似于静态的数据成员,公有的、静态的成员函数在类外的调用方式为:
  类名::成员函数名(实参表)
不过,也允许用对象调用静态的成员函数。

下面举一个例子说明静态成员函数的调用方法:


例10-22
class Directory
{
 public:
  ...
  // 静态公有的函数
  static void setpath(char const *newpath);

 private:
  // 静态字符串
  static char path [];
};

//静态数据成员的初始化
char Directory::path [199] = "/usr/local";

// 静态函数
void Directory::setpath(char const *newpath)
{
 strncpy(path, newpath, 199);
}

// 使用实例
void main()
{
 // 通过类名调用setpath()成员函数
 Directory::setpath("/etc");

 //通过象调用setpath()成员函数
 Directory dir;
 dir.setpath("/etc");
}

  在上面的例子中, setpath()始一个公有静态的成员函数,C++也允许私有静态的成员函数,它只能被该类的其它成员函数调用,不能在类外被调用。
  需要注意的是:静态的成员函数仅能访问静态的变量和静态的成员函数,不能访问非静态的数据成员和非静态的成员函数。
静态成员函数
  我们也可以创建一个静态的成员函数。同静态数据成员类似,它也是属于类,而不是属于某一个对象的。
  静态成员函数不能访问非静态的数据成员,它只能访问静态数据成员,也只能调用其它的静态成员函数。原因是:当前对象的地址(this)是被隐含地传递到被调用的函数的。但一个静态成员函数没有this指针,所以它无法访问非静态的成员函数。
  下面是一个应用静态数据成员和静态成员函数的例子:



例10-23
class X
{
 int i;
 static int j;
public:
 X(int I = 0):i(I)
 {
  //非静态成员函数可以访问
  //静态成员变量和成员函数
  j = i;
 }

 int val()const
 {
  return i;
 }

 static int incr()
 {
  //i++;   //Error: 静态成员函数
        //不能访问非静态成员变量
  return ++j; //OK
 }

 static int f()
 {
  //val();    //Error: 静态成员函数
          //不能访问非静态成员函数
  return incr(); //OK
 }
};

int X::j = 0;

void main()
{
 X x;
 X* pX = &x;
 x.f();
 pX -> f();
 X::f();
}

  由于静态成员函数是属于类的,所以,静态成员函数的调用方法为:
  类名::静态成员函数名();
但也允许用对象或指向对象的指针调用。例如,在main()中,静态成员函数f分别被对象x和指向x对象的指针pX调用。

下面是一个有趣的例子:类egg有一个静态数据成员E和静态成员函数instance,instance的返回类型是指向egg对象的指针,类的构造函数是私有的。显然,我们不能创建该类的对象。但是该类确有一个对象存在,我们可以访问那个对象,也可以通过该对象访问其成员函数:



例10-24
#include <iostream.h>
class egg
{
 static egg E;
 int i;
 egg(int I):i(I){}
public:
 static egg* instance()
 {
  return &E;
 }
 int val()
 {
  return i;
 }
};

egg egg::E(100);

void main()
{
 //egg x(1); //Error: 不能调用私有的构造函数
 //因此,不能创建新的对象
 cout<<egg::instance()->val()<<endl;
}

  E 的初始化出现在类的声明完成后,所以编译器已有足够的信息为对象分配空间并调用构造函数。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: