Шаг 8.
Статические компоненты класса

    На этом шаге мы рассмотрим особенности использования статических компонентов класса.

    Статический компонент класса уже использовался в классе goods, где ко всем объектам класса относилась переменная static int percent - торговая наценка. Рассмотрим эту возможность подробнее.

    Каждый объект одного и того же класса имеет собственную копию данных класса. Можно сказать, что данные класса тиражируются при каждом определении объекта этого класса. Отличаются они друг от друга именно по "привязке" к тому или иному объекту. Это не всегда соответствует требованиям решаемой задачи. Например, при формировании объектов класса может потребоваться счетчик объектов. Если объекты создаются и при этом сцепляются в цепочку, образуя связный список, то для просмотра всего списка удобно иметь указатель на начало списка. Для добавления нового объекта в конец такого списка нужен указатель на последний элемент списка (на последний объект, включаемый в список). Такие указатели на первый и последний объекты списка, а также уже упомянутый счетчик объектов можно сделать компонентами класса, но иметь их нужно только в единственном числе (каждый).

    Чтобы компонент класса был в единственном экземпляре и не тиражировался при создании каждого нового объекта класса, он должен быть определен в классе как статический, т.е. должен иметь атрибут static.

    Статические компоненты класса после инициализации можно использовать в программе еще до определения объектов данного класса. Такую возможность для общедоступных данных предоставляет квалифицированное имя компонента. Когда определен хотя бы один объект класса, к его статическим компонентам можно обращаться, как к обычным компонентам, т.е. с помощью операций выбора компонентов класса ('.' и '->'). Здесь возникает одно затруднение. На статические данные класса распространяются правила статуса доступа. Если статические данные имеют статус private или protected, то к ним извне можно обращаться через компонентные функции. При каждом вызове такой компонентной функции необходимо указать имя некоторого объекта. К моменту обращения к статическим данным класса объекты класса могут быть либо еще не определены, либо их может быть несколько и каждый пригоден для вызова компонентных функций. Без имени объекта обычную компонентную функцию вызвать нельзя в соответствии с требованиями синтаксиса. Но какой объект выбрать для вызова, ведь каждый статический элемент класса единственный в нем? Хотелось бы иметь возможность обойтись без имени конкретного объекта при обращении к статическим данным класса.

    Проиллюстрируем описанную проблему на конкретном примере. Пусть имеется следующий класс:

  class A { 
     static int N; 
      .    .    .    .
  };
который используется в программе:
      .    .    .    .
  //Инициализация статического элемента.
  int A::N = 0;
      .    .    .    .
  void main() 
  { 
     //Здесь нельзя использовать значение переменной N,
     //так как нет еще объектов данного класса.
      .    .    .    .
     A b,c; //Создано два объекта класса A.
            //Какой использовать для доступа 
            //к статическом данному класса?  
      .    .    .    .
  };

    Возможность использования статических данных класса без создания объектов этого класса обеспечивают статические компонентные функции.

    Статическая компонентная функция сохраняет все основные особенности обычных (нестатических) компонентных функций. К ней можно обращаться, используя имя уже существующего объекта класса либо указатель на такой объект. Дополнительно статическую компонентную функцию можно вызвать, используя квалифицированное имя:

        имя_класса::имя_статической_функции

    С помощью квалифицированного имени статические компонентные функции можно вызывать до определения конкретных объектов класса и не используя конкретных объектов. В следующей программе класс point3 определяет точку в трехмерном пространстве и одновременно содержит статический счетчик N таких точек. Обращение к счетчику обеспечивает статическая компонентная функция count().

//OOР8_1.СРР - статические компоненты класса.
#include <iostream.h>
class point3     // Точка в трехмерном пространстве.
 { double x, y, z; // Координаты точки.
	static int N;   // Количество точек (счетчик).
	public:
	 // Конструктор инициализирует значения координат:
	 point3(double xn = 0.0, double yn = 0.0,
		  double zn = 0.0)
	 { N++; x = xn; y = yn; z = zn; }
	 // Обращение к счетчику:
	 static int& count() { return N; }
  };
 // Внешнее описание и инициализация статического элемента:
 int point3::N = 0;
 void main(void)
 {
   cout << "\nsizeof(point3) = " << sizeof(point3);
	point3 A(0.0,1.0,2.0);
	cout << "\nsizeof(A) = " << sizeof(A);
	point3 B(3.0,4.0,5.0);
	cout << "\nОпределены " << point3::count() << " точки.";
	point3 C(6.0,7.0,8.0);
	cout << "\nOnpeделены   "  << B.count() << " точки.";
 }
Текст этой программы можно взять здесь.

    Результаты выполнения программы:

   sizeof(point3) = 24 
   sizeof(A) = 24 
   Определены 2 точки. 
   Определены 3 точки.


    Замечание. Обратите внимание, что размер типа point3 равен размеру одного объекта этого класса. Память выделена для трех элементов типа double, и никак не учтено наличие в классе статического компонента int N.

    Как уже говорилось, в отличие от обычных компонентных данных статические компоненты класса необходимо дополнительно описывать и инициализировать вне определения класса как глобальные переменные. Именно таким образом в приведенной выше программе получает начальное значение статический элемент класса point3::N. Так как N - собственный компонент класса, то последующие обращения к нему возможны только с помощью дополнительной общедоступной функции. В данном примере это статическая функция count(). Попытка обратиться к компоненту N с помощью квалифицированного имени point3::N будет воспринята как ошибка, так как для N определен статус private.

    Нестатический компонент класса может быть указателем или ссылкой на объект того же класса. Такая возможность позволяет формировать связные списки (цепочки) объектов одного класса. Статическим компонентом класса может быть указатель на объект класса. Это позволит однозначно определить начало связного списка объектов класса. Например, для моделирования списка можно ввести класс такой структуры:

   class list
   { ...   // Собственные компоненты.
     public:
      static list* begin;  // Начало связного списка.
     .    .    .
   };
   .    .    .
   list* list::begin = NULL; // Инициализация статического
                             // компонента.
   .    .    .

    Полное определение класса, моделирующего список, будет приведено позднее, в шаге, где рассматривается указатель this.

    На следующем шаге мы рассмотрим использование указателей на компоненты класса.




Предыдущий шаг Содержание Следующий шаг