Обычно каждый объект класса имеет свою собственную копию всех дан- ных-элементов класса. Но в определенных случаях во всех объектах класса должна фигурировать только одна копия некоторых данных-элементов для всех объектов класса. Для этих и других целей используются статические данные-элементы, которые содержат информацию «для всего класса». Объявление статических элементов начинается с ключевого слова static.
Если вам достаточно единственной копии данных, используйте для сокращения затрат
памяти статические данные-элементы.
Хотя может показаться, что статические элементы данные похожи на глобальные переменные, тем не менее они имеют областью действия класс. Статические элементы могут быть открытыми, закрытыми или защищенными (protected). Статическим данным-элементам можно задать начальные значения один (и только один) раз в области действия файл. Доступ к открытым статическим элементам класса возможен посредством любого объекта класса или посредством имени класса, с помощью бинарной операции разрешения области действия. Закрытые и защищенные статические элементы класса должны быть доступны открытым функциям-элементам этого класса или друзьям класса. Статические элементы класса существуют даже тогда, когда не существует никаких объектов этого класса. Чтобы в этом случае обеспечить доступ к открытому статическому элементу, просто поставьте перед элементом данных имя класса и бинарную операцию разрешения области действия. Для обеспечения доступа в указанном случае к закрытому или защищенному элементу класса должна быть предусмотрена открытая статическая функ- ция-элемент, которая должна вызываться с добавлением перед ее именем имени класса и бинарной операции разрешения области действия.
Программа на рис. 7.9 демонстрирует использование закрытого статического элемента данных и открытой статической функции-элемента. Элементу данных count задается нулевое начальное значение в области действия файл с помощью оператора
int Employee::count = 0;
Элемент данных count обслуживает подсчет количества объектов класса Employee, которые были созданы. Если объекты класса Employee существуют, элемент count может быть вызван посредством любой функции-элемента объекта Employee (в данном примере посредством как конструктора, так и деструктора). Если никаких объектов класса Employee не существует, элемент count также может быть вызван, но только посредством вызова статической функции-элемента getCount следующим образом:
Employee::getCount()
В этом примере функция getCount использована для определения текущего числа созданных объектов класса Employee. Отметим, что когда в программе еще не создано ни одного объекта, используется вызов функции Ет- ployee::getCount(). Но когда объекты уже созданы, функция getCount может быть вызвана из одного из объектов оператором
elPtr->getCount()
// ENPLOY1.H // Класс employee #ifndef EMPL0Y1_H #define EMPLOY1 H
class Employee { public:
Employee(const char*, const char*); -Employee(); // деструктор
const char *getFirstName() const; const char *getLastName() const; // статическая функция-элемент static int getCount();
private:
char *firstName; char *lastName;
// конструктор
// возвращает имя
// возвращает фамилию
// возвращает число
// созданных объектов
// число созданных объектов |
// статический элемент данных static int count;
} ;
#endif
Рис. 7.9. Использование статического элемента данных для подсчета количества объектов
класса (часть 1 из 5)
// EMPLOYl.CPP
// Определения функций-элементов класса Employee
finclude
finclude
finclude
finclude "employl.h"
// Задание начального значения элемента данных int Employee::count = 0;
// Определение статической функции-элемента, которая // возвращает количество созданных объектов, int Employee::getCount() { return count; }
// Конструктор динамически выделяет память для // имени и фамилии и использует strcpy, чтобы //скопировать имя и фамилию в объект.
Employee::Employee(const char *first, const char *last) {
firstName = new char[ strlen(first) + 1 ];
assert(firstName != 0); // проверка выделения памяти
strcpy(firstName, first);
lastName = new char[ strlen(last) + 1 ];
assert(lastName != 0); // проверка выделения памяти
strcpy(lastName, last);
++count; // увеличение статического счетчика
// служащих
cout « "Конструктор Employee для " << firstName « ' ' « lastName « " вызван." « endl;
// Деструктор освобождает динамически выделенную память
<< firstName // освобождение памяти // освобождение памяти // уменьшение статического // счетчика служащих |
Employee::~Employee() {
cout << "~Employee() вызван для << ' ' « lastName «endl; delete [ ] firstName; delete [ ] lastName; —count;
// Возвращение имени служащего
const char *Employee::getFirstName() const {
// Const перед возвращаемым типом предотвращает изменение //клиентом закрытых данных. Клиент должен скопировать //возвращенную строку перед тем, как деструктор освободит //динамическую память; это позволит избежать неопределенного // указателя, return firstName;
I }
Рис. 7.9. Использование статического элемента данных для подсчета количества объектов
класса (часть 2 из 5)
// EMPL0Y1.CPP: Продолжение // Возвращение фамилии служащего
const char *Employee::getLastName() const {
// Const перед возвращаемым типом предотвращает изменение //клиентом закрытых данных. Клиент должен скопировать //возвращенную строку перед тем, как деструктор освободит //динамическую память; это позволит избежать // неопределенного указателя, return lastName;
.... }
Рис. 7.9. Использование статического элемента данных для подсчета количества объектов
класса (часть 3 из 5)
// FIG7 9.СРР —
// Драйвер для проверки класса employee Iinclude <iostream.h> Iinclude "employl.h"
main() {
cout << "Количество служащих перед созданием объектов равно " << Employee::getCount() << endl; // используется
// имя класса
Employee *elPtr = new Employee("Susan" , "Baker" );
Employee *e2Ptr = new Employee("Robert", "Jones");
cout << "Количество служащих после создания объектов равно " « elPtr->getCount() « endl;
cout << endl << "Служащий 1: " « elPtr->getFirstName() « " " « elPtr->getLastName() « endl « "Служащий 2: " << e2Ptr->getFirstName()
« " " « e2Ptr->getLastName() « endl « endl;
delete elPtr; // освобождение памяти
delete e2Ptr; // освобождение памяти
cout << "Количество служащих после удаления равно " << Employee::getCount() << endl; return 0;
Рис. 7.9. Использование статического элемента данных для подсчета количества объектов
класса (часть 4 из 5)
Функция-элемент тоже может быть объявлена как static, если она не должна иметь доступ к нестатическим элементам класса. В отличие от нестатических функций-элементов статическая функция-элемент не имеет указателя this, потому что статические данные-элементы и статические функции-элементы существуют независимо от каких-либо объектов класса.
|
Количество служащих перед созданием объектов равно О Конструктор Employee для Susan Baker вызван.
Конструктор Employee для Robert Jones вызван.
Количество служащих после создания объектов равно 2
Служащий 1: Susan Baker Служащий 2: Robert Jones ~Employee() вызван для Susan Baker ~Employee() вызван для Robert Jones Количество служащих после удаления равно О
Рис. 7.9. Использование статического элемента данных для подсчета количества объектов
класса (часть 5 из 5)
Отметим использование assert в функции конструкторе Employee, функции getFirstName и функции getLastName. Утилита assert, определенная в заголовочном файле assert.h, проверяет значение выражения. Если значение выражения равно О (ложь), то assert печатает сообщение об ошибке и вызывает функцию abort (из общей библиотеки утилит — stdlib.h), которая завершает выполнение программы. Это полезное отладочное средство для проверки, имеет ли переменная правильное значение. В этой программе assert определяет, способна ли операция new осуществить динамическое выделение необходимого объема памяти. Например, в функции конструкторе Employee следующая строка (называемая также оператором контроля)
assert(firstName !=0);
проверяет указатель firstName, чтобы определить, не равен ли он О. Если условие в операторе контроля истинно, то программа продолжается без прерывания. Если же это условие ложно, то выдается сообщение об ошибке, содержащее номер строки, проверяемое условие, печатается имя файла, в котором проявил себя оператор контроля, и программа завершается. Программист может сосредоточить свое внимание на этой части кода, чтобы найти причину ошибки. В главе 13 «Обработка исключений» мы узнаем более совершенные методы работы с ошибками во время выполнения.
Операторы контроля не надо удалять из программы после окончания отладки. Когда операторы контроля больше не нужны для отладки программы, в начале файла программы вставляется строка
ttdefine NDEBUG
Это приводит к тому, что препроцессор игнорирует все операторы контроля, что удобнее, чем удалять каждый такой оператора контроля вручную.
Типичная ошибка программирования 7.9
Ссылка на указатель this внутри статической функции-элемента.
Типичная ошибка программирования 7.10
Объявление статической функции-элемента как const.
Замечание по технике программирования 7.7
Статические данные-элементы и статические функции-элементы существуют и могут бьль использованы, даже если не создано никаких объектов соответствующего класса.
Заметим, что реализации функций getFirstName и getLastName возвращают клиенту класса постоянные указатели на символьные строки. Если клиент желает сохранить копию имени или фамилии, он должен скопировать динамически распределенную область памяти объекта класса Employee после того, как получит от объекта постоянный указатель на символьную строку. Заметим, что можно также реализовать getFirstName и getLastName так, что от клиента будет требоваться передавать каждой функции массив символов и его размер. Тогда функции могли бы копировать имя и фамилию в массив символов, который передал им клиент.
Комментариев нет:
Отправить комментарий