    Глава 13

    Программирование классов 

    Вы, наверное, жили в лунном кратере, никогда не высовываясь наружу, если 
ничего не слышали об объектно-ориентированном программировании, или ООП (как 
его еще шутливо сокращенно называют)! ООП — парадигма программирования 1990-х, 
модель (как кое-кто утверждает), на основе которой скоро будет создаваться 
все программное обеспечение в мире. Правда, еще остались противники перехода 
на ООП, а также сомневающиеся в его необходимости, но, похоже, предсказание о 
повсеместном внедрении ООП начинает сбываться, и тот, кто игнорирует ООП, 
рискует упустить основное достижение в программировании на языке C++ — класс. 
    Короче говоря, класс — это объектно-ориентированный инструмент для 
создания новых типов данных. В C++ существует множество встроенных типов 
данных, таких как int, double и char. Классы дают вам возможность определять 
новые типы данных, которые согласно принципам ООП в использовании почти не 
отличаются от встроенных. 

    Почему необходимо использовать объектно-ориентированное  программирование 

    Многие эксперты соглашаются с тем, что ООП и классы C++ помогают уменьшить 
сложность, особенно в больших по размерам программах в 10,000 и более строк. 
ООП также способствует повторному использованию написанного ранее кода, вместо 
переписывания функций. Обычная практика программистов на С — написание одного 
и того же кода снова и снова, поскольку слишком хлопотно использовать старые 
проверенные модули. Классы C++ легко повторно использовать и расширять, и код 
ООП имеет тенденцию эволюционировать из написанных модулей подобно тому, как 
растет дерево с ростом новых ветвей. Вместо повторного выращивания 
низкоуровневого кода в каждой новой программе, с помощью ООП вы можете писать 
программы, вырастающие из собранного ранее "урожая" проверенных модулей. 
    Если вы впервые имеете дело с ООП, не удивляйтесь тому, что поначалу его 
преимущества минуют ваше понимание. C++ позволит вам вникнуть в ООП достаточно 
быстро. В отличие от так называемых "чистых" языков ООП, таких 

    305 (192) 

как SmallTalk и Actor, C++ — гибридный язык программирования, который 
сочетает в себе качества обычного и объектно-ориентированного подходов. Это 
означает, что при изучении ООП вы можете пользоваться тем, что вам уже 
известно о C++. Вам нет надобности одновременно вникать во все детали ООП. 
    В этой главе, как и в главах 14 и 15, приводится основная информация о 
классах в C++ и поясняется, какое отношение имеют классы к трем основным 
преимуществам ООП. 

    •	Инкапсуляция 
    •	Наследование 
    •	Виртуальные функции 

    В этой главе вы узнаете, как инкапсуляция объединяет данные и функции в 
классе. В следующей главе пойдет речь о наследовании и виртуальных функциях.
    Для лучшего понимания важности инкапсуляции совершим краткий экскурс 
назад, в программирование на С. Классы C++ и инкапсуляция помогут справиться с 
наиболее часто возникающей проблемой в традиционном программировании — 
изменением фундаментальной структуры данных в почти завершенной программе. 

    Скандал в раю 

    В традиционном программировании программистам часто приходится трудиться 
больше, чем это необходимо. Например, рассмотрим, как можно представить дату и 
время — это типичная задача, которую приходилось решать многим, если не 
большинству программистов. В ANSI С вы объявили бы структуру, подобную следующей: 

    struct TTime { 
      int year; 
      int month; 
      int day; 
      int hour; 
      int minute; 
   }; 

    Члены TTime сохраняют компоненты значений даты и времени. Лишнее Т в TTime 
означает тип (type) — это соглашение, которое помогает отличать имена типов 
данных от прочего. В соответствии с этой структурой вы можете объявить 
переменную типа TTime следующим образом: 

   TTime appointment; 

    Заметьте, что C++ позволяет опустить ключевое слово struct перед TTime, 
обязательное в ANSI С. В соответствии с данным объявлением вы можете присвоить 
значения членам переменной appointment с помощью следующих операторов: 

    appointment.year = 1996; 
    appointment.month = 7; 
    appointment.day = 14; 
    appointment.hour = 8; 
    appointment.minute = 30; 

    В программе, использующей множество структур TTime, вероятно, потребуются 
функции вывода даты и времени, изменения членов структуры, сравнения двух дат 
и т.п. Вы можете написать следующую функцию отображения: 

    void Display(struct TTime *tp) 
    { 
    char buffer[32] ; 
    sprintf( buffer,   "Date: %02d/%02d/%04d Time: %02d:%02d\n",  tp->month,  
    tp->day,  tp->year,  tp->hour,  tp->minute ) ; 
    cout << buffer ; 
    } 

    Чтобы отобразить дату и время, можно передать в операторе вызова функции 
Display() адрес структуры appointment типа TTime: 

    Display ( & appointment ) ; 

    Пока "в раю" все прекрасно. Не принимая во внимание, конечно, типичной 
проблемы, которая может превратить в ничто весь написанный код. После создания 
структуры, подобной TTime, и написания тьмы-тьмущей функций, работающих с 
датами и временем, вы обнаруживаете превосходный способ запоминания даты и 
времени, который значительно повысит скорость выполнения вашей программы. 
Например, вы можете использовать длинное целое для хранения количества секунд 
от фиксированной даты. Это изменение также даст возможность пользоваться 
библиотечными функциями, работающими с датой и временем и распознающими такой 
способ их запоминания. Плохо только, что эта идея не посетила вас раньше! Для 
изменения внутреннего представления данных в конечной стадии написания 
программы вам придется сделать следующее. 
    
    306	Часть III. Программирование на ANSI C++ 

    •   Изменить структуру TTime путем удаления старых членов и добавления 
одного-единственного нового длинного целого. (Вы даже можете не использовать 
структуру вообще.) 
    •   Переделать все функции, подобные Display(),  которые используют 
параметры TTime или возвращают структуру TTime, указатель или ссылку на нее. 
    •   Выловить и модифицировать все операторы, в которых используются 
компоненты структуры TTime. Эти операторы необходимо переделать для 
использования нового формата даты и времени. В больших программах, состоящих 
из тысяч строк, перспектива выслеживания каждого использования TTime приводит 
в уныние. Изменения в структуре данных вынуждают вас переделывать код, который 
уже был протестирован. Таким образом, потребуется повторная отладка и потеря 
драгоценного времени. Старое эмпирическое правило гласит: данные формируют 
код.  Конечно, в реальной жизни спецификации данных имеют тенденцию к 
изменению во время разработки программы, задавая лишнюю работу программистам. 
    В традиционном программировании внутреннее представление данных 
определяется на ранней стадии разработки, что ограничивает свободу внесения в 
него последующих изменений. В ООП форма представления данных может меняться, в 
дальнейшем оказывая ограниченные воздействия на код. Классы C++ могут 
запретить доступ к тонкостям внутреннего представления данных, и изменения 
представления данных не обязательно повлияют на каждое использование этой информации. 

    Введение в понятие класса 


           Класс 
    Функции-члены 
    Данные-члены 

                  Рис. 13.1. Инкапсуляция данных и функций в классе 

    Рассмотрим возможное представление даты и времени в классе и его 
преимущества. Класс — это вид структуры, инкапсулирующей данные и функции в 
удобную упаковку (рис. 13.1). 
    Приведем предыдущую структуру TTime в виде класса с инкапсулированными 
функциями и данными: 

    class TTime	{ 
    public:			// Спецификатор доступа 
      int year ;		// Данные-член 
      int month ;		//             "             " 
      int day ;		//             "             "                                     
      int hour ;		//             "             "                                   
      int minute;		// 
      void Display(void) ;	// Функция-член 
    }; 

    Объявление класса очень похоже на объявление структуры TTime, исключая тот 
факт, что оно начинается с ключевого слова class. Опять же, по соглашению 
лишняя буква Т в имени класса напоминает вам, что ТТуре — тип данных. В 
скобках заключены шесть членов класса, которым предшествует слово public, и 
один из трех спецификаторов доступа, которые контролируют возможность 
использования членов класса в программе. Позже вы ознакомитесь с двумя другими 
спецификаторами доступа — private и protected. Спецификатор доступа public 
открывает доступ ко всем членам, следующим за ним, для всех пользователей 
класса, поэтому такие члены называются открытыми. 
    Данные-члены в классе в точности такие же, как и данные-члены в структуре. 
Данные-члены, такие как year, month и day, могут быть переменными, 
указателями, ссылками, массивами, структурами и т.п. Они могут быть также и 
объектами класса. Конечно, члены не могут использовать спецификаторы классов 
памяти auto, extern или register. 
    Функции-члены класса, такие, как Display() объявлены прототипами функций. 
Предположительно, функции-члены, подобные Display() в TTime, выполняют 
некоторые операции над данными-членами класса. Тело функции размещается в 
другом месте программы. 
    В листинге 13.1 используется класс TTime в программе на C++ и 
демонстрируется выполнение функции-члена. Скомпилируйте программу и запустите 
ее для отображения образца даты и времени. 

    Листинг 13.1.  CLASS.СРР (класс TTime) 

    1:	#include <iostream.h> 
    2:	#include <stdio.n> 
    3: 
    4:	class TTime { 
    5:	public: // Спецификатор доступа 
    6:	  int year; // Данные-члены 
    7:	  int month; // " " 
    8:	  int day; // " " 
    9: 	  int hour; // " " 
    10:	  int minute; // " " 
    11:	  void Display(void); // Функция-член 
    12:	}; 
    
    
    Глава 13. Программирование классов	307(194) 

    14:	main() 
    15:	{ 
    16:	TTime appointment;     // Объект типа TTime 
    18:	appointment.month = 7; // Инициализация данных-членов объекта 
    19:	appointment.day =14;   
    20:	appointment.year = 1996; 
    21:	appointment.hour = 8; 
    22:	appointment.minute = 30; 
    23:	cout << "Appointment == "; 
    24:	appointment.Display() ; // Вызов функции-члена 
    25:	cout << '\n' ; 
    26:	return 0;                           , 
    27:	} 
    28: 
    29:	void TTime::Display(void) 
    30:	{ 
    31:	char s[32]; 
    32:	sprintf(s, "Date: %02d/%02d/%04d Time: %02d:%02d\n", 
    33:	month, day, year, hour, minute); 
    34:	cout << s ; 
    35:     }

    В строках 4-12 объявляется класс TTime. Обычно объявления классов пишутся 
в заголовочных файлах, но вы можете вставить его непосредственно в программный 
модуль, как в этом примере.
    Пропустим все до строк 29-35, где приводится реализация тела функции 
Display(). Существует два основных отличия между описанием функции-члена и 
обычной функции. 
    •   Имени функции-члена предшествует имя класса и оператор разрешения 
области видимости. Заголовок функции void TTime::Display(void) указывает 
компилятору, что Display() — член класса TTime. Поскольку имя класса однозначно 
отождествляет имя функции-члена, в программе могут быть другие функции и 
функции-члены класса с именем Display(), не вызывающие конфликтов имен. 
    •   Внутри функции-члена операторы имеют прямой доступ к членам класса. 
Например, в строках 32-33 непосредственно используются данные-члены класса 
TTime — month, year, hour и minute. 
    В основной программе класс TTime используется так же, как и любой другой 
тип данных. Сначала в строке 16, объявляется appointment, как объект типа 
TTime. Объявление создает объект класса, так называемый экземпляр типа класса. 
Класс TTime — всего лишь шаблон, схема, описывающая формат членов класса. Вы 
должны создать объект типа класс, подобный appointment, для того, чтобы 
использовать класс. 
    Далее, в строках 18-22 присваиваются значения данным-членам appointment, 
инициализируются дата и время. Затем в строке 24, вызывается функция-член 
Display(), отображающая дату и время, хранящиеся в appointment. Все эти строки 
используют оператор точки для доступа к членам объекта класса. Оператор точки 
следует использовать для ссылок на открытые данные-члены класса (которым 
предшествует спецификатор доступа public) аналогично ссылкам на члены 
структуры. Если таким членом является функция (см. строку 24), C++ вызывает 
функцию, которая обычно выполняет некоторые операции над данными-членами 
объекта класса. Например, в строке 24 вызывается функция Display() для 
отображения значения данных-членов appointment. В программе также могут быть 
другие объекты класса TTime, объявленные следующим образом: 

    TTime today; 
    TTime tomorrow; 
    TTime yesterday; 

    Этим объектам можно присвоить значения, и затем отобразить с помощью таких операторов: 

    today.Display();		// Отображение значения объекта класса today 
    tomorrow.Display();	// Отображение значения объекта класса tomorrow 
    yesterday.DisplayO;	// Отображение значения объекта класса yesterday 


    ЗАМЕЧАНИЕ 
    C++ позволяет структурам и объединениям иметь функции-члены, подобно 
классам. Действительно, если вы замените class на struct и удалите 
спецификатор доступа public, программа будет работать, как и раньше. (Члены 
структуры или объединения по умолчанию открыты для доступа). Конечно, на 
практике функции-члены: очень редко используются в структурах и объединениях и 
обычно объявляются в классах. 

    В CLASS1.CPP ничего не предпринимается для предупреждения проблемы 
изменения представления данных, упоминаемой ранее. Все данные-члены в классе 
TTime открыты для доступа и, следовательно, доступны для непосредственного 
использования в операторах, подобных тем, что приведены в строках 18-22. Измене-


    308 (195)   Часть III. Программирование на ANSI C++ 

ние данных-членов класса повлечет за собой изменение всех таких операторов 
— это излишняя работа, которая отодвинет сроки завершения программы. 
    В листинге 13.2 объявляется новая улучшенная версия класса TTime, которая 
использует функции-члены для доступа к данным-членам класса. Новый класс 
облегчает изменение структуры данных-членов класса, что позволяет выдержать сроки 
проекта. 
 
    Листинг 13.2.  CLASS2.CPP (новая улучшенная версия класса TTime)

    1:	#include <iostream.h> 
    2:	#include <stdio.h> 
    3:	 
    4:	class TTime { 
    5:	private:
    6:             int year ; 
    7:             int month ; 
    8:             int day; 
    9:             int hour; 
    10:           int minute; 
    11:	public: 
    12:           void Display(void) ; 
    13:           void GetTime( int &m, int &d, int &y, int &hr, int &min ) ; 
    14:           void SetTime( int m, int d, int y, int hr, int min ) ; 
    15:	}
    16: 
    17:	main() 
    18:	{ 
    19:	TTime appointment; 
    20:	int month, day, year, hour, minute; 
    21: 
    22:	appointment.SetTime(7, 14, 1996, 8, 30); 
    23:	cout << "Appointment == ";                      . 
    24:	appointment.Display() ; 
    25:	appointment.GetTime( month, day, year, hour, minute ) ; 
    26:	appointment.SetTime( month, day, year, hour, minute ) ; 
    27:	cout << "Next hour == ";
    28:	appointment.Display() ; 
    29:	return 0 ; 
    30:	} 
    31:
    32:	void TTime::Display(void) 
    33:	{ 
    34:	char s[32]; 
    35:	sprintf(s, "Date: %02d/%02d/%04d Time: %02d:%02d\n", 
    36:	month, day, year, hour, minute); 
    37:	cout << s; 
    38:	} 
    39: 
    40:	void TTime::GetTime(int &m, int &id, int &y, int &ihr, int &min) 
    41:	{ 
    42:	m = month; // Возвращение данных-членов тому, кто вызвал функцию 
    43:	d = day ; 
    44:	у = year ; 
    45:	hr = hour ;
    46:	min = minute ; 
    47:	} 
    49:	void TTime::SetTime(int m, int d, int y, int hr, int min) 
    50:	{ 
    51:	month = m; // Присваивание аргументов данным-членам 
    52:	day = d; 
    53:	year = y; 
    54:	hour = hr; 
    55:	minute = min; 
    56:	}

    В новом классе TTime — три дополнения. Во-первых, спецификатор доступа 
private (строка 5) делает закрытыми в классе данные-члены в строках 6-10. 
Благодаря этому изменению только функции-члены класса могут непосредственно 
ссылаться на эти данные-члены. Запомните следующее правило: закрытые (private) 
члены класса доступны только членам этого класса и никому более, закрытые 
члены невидимы вне класса. 
    
    
    Глава 13. Программирование классов	                                   309 (196) 


    ЗАМЕЧАНИЕ 
    Члены класса закрыты по умолчанию, и, действительно; строка 5 в 
CLASS2.CPP — лишняя. Конечно, для внесения ясности в программу отмечать 
закрытые члены с помощью недвусмысленного спецификатора доступа private — 
неплохая идея. 

    В классе могут присутствовать многочисленные открытые и закрытые секции, 
располагающиеся в произвольном порядке. Вы можете повторять спецификаторы 
доступа private и public в объявлении класса (строки 5 и 11) столько раз, 
сколько понадобится. Хотя это не показано в примере, функции-члены также могут 
быть объявлены в закрытой части, но обычно они открыты. Вы также узнаете о 
третьем спецификаторе доступа, protected, в главе 14. 
    Два других дополнения к TTime — функции-члены GetTime() и SetTime() (строки 
13-14). Вместе с Display() три функции-члена ограничивают доступ к закрытым 
частям класса. Для использования закрытых данных-членов TTime существует лишь 
один законный способ — вызов открытых функций-членов класса. Непосредственный 
результат инкапсуляции данных и функций в классе — при изменении представления 
данных требуется переписать лишь функции-члены класса. Таким образом, если 
возникла подобная проблема, отладка может начаться с исследования 
функций-членов класса, поскольку не существует других функций или операторов, 
в которых осуществляется непосредственный доступ к закрытым данным-членам 
класса. На рис. 13.2 демонстрируется эта концепция, стрелочками отмечены пути 
доступа к открытым и закрытым членам класса. Заметьте, что функции в классе 
имеют доступ друг к другу, т.е. две функции класса могут вызывать одна другую. 
    В строках 40-56 вводятся две новые функции-члена — GetTime() и SetTime(). 
Как и Display(), имя класса и оператор разрешения области видимости TTime:: 
предшествует именам функций, что указывает компилятору на принадлежность этих 
функций классу. В программе могут быть объявлены другие функции и 
функции-члены с именем GetTime() и SetTime() в других классах без конфликтов с 
принадлежащими TTime. 
    В телах функций-членов операторы имеют непосредственный доступ к 
данным-членам класса month, day, year, hour и minute (рис. 13.2.) Например, в 
GetTime(), в строках 42—46 передаются значения компонентов объекта класса 
обратно, в аргументы оператора вызова, путем использования пяти целочисленных 
ссылочных параметров. В SetTimeO значения параметров присваиваются 
данным-членам класса. 

    
    Рис. 13.2. Классы ограничивают доступ к закрытым членам 

    В основной программе, поскольку данные-члены TTime закрыты в классе, 
больше невозможно присваивать значения непосредственно объекту класса. 
Например, оператор из CLASS1.CPP 

    appointment.month = 7;	// ??? 

вызвал бы сообщение об ошибке при компиляции CLASS2.CPP, так как month 
закрыт в классе TTime и поэтому может непосредственно использоваться только 
членами этого класса Если вы попытаетесь скомпилировать этот оператор, 
компилятор "пожалуется", что "TTime::month is not accessible in function 
main()" (TTime::month не доступен в функции main()). 

    310 (197)                        Часть III. Программирование на ANSI C++ 

    В новой программе для присваивания значений объекту класса appointment 
потребуется вызвать функцию-член. Например, в строке 22 выполняется оператор, 
который устанавливает дату и время в appointment равными 14 июля 1996 года, 8:30: 

	    appointment.SetTime(7, 14, 1996, 8, 30); 

    Аналогично, в строке 25 вызывается функция GetTime()для копирования 
значений данных-членов appointment в локальные переменные, объявленные в 
строке 20. Эти значения затем передаются обратно SetTime() с увеличенным на 1 
значением hour, что устанавливает в appointment следующий час. (Очевидно, это 
не идеальный путь изменения даты и времени, но вы позаботитесь об этом позже.) 

    Использование функций-членов 

    Большинство классов лучше объявлять в заголовочных файлах, которые затем 
могут включаться в многочисленные программные модули для совместного 
использования одинаковых классов. Следующие несколько листингов расширяют 
класс TTime и демонстрируют обычный способ объявления и включения классов и 
программ в нескольких модулях. 
    В листинге 13.3 объявляется новый класс TTime, схожий с объявленным в 
CLASS2.CPP, но с парочкой дополнительных функций-членов. Как и прежде, новый 
класс имеет несколько закрытых данных-членов. 
    Подобно большинству проектов, находящихся в стадии разработки, эта 
программа не лишена некоторых недостатков. Но это не имеет значения, поскольку 
проблема изменения внутреннего представления данных в классе легкоразрешима, и 
вы это вскоре увидите. 
    Как и все порядочные заголовочные файлы, TIME1.H — не программа, вы не 
можете скомпилировать и запустить ее на выполнение. Другие программные модули 
(листинги 13.4 и 13.5) включают этот заголовочный файл, делая объявления, 
содержащиеся в нем, доступными для операторов. Применение заголовочных файлов 
— не обязательный, но рекомендуемый способ улучшения организации частей программы. 

    Листинг 13.3. TIME1.H (объявление класса TTime в заголовочном файле) 

    1:	// timel.h -- Объявление класса TTime 
    2: 
    3:	#ifndef __TIME1_H 
    4:	#define __TIME1_H 1 // Предотвращение нескольких #include 
    5: 
    6:	class TTime { 
    7:	private: 
    8:	  int month; 
    9:	  int day; 
    10:	  int year; 
    11:	  int hour; 
    12:	  int minute; 
    13:	public: 
    14:	  void Display(void); 
    15:	  void GetTime(int &m, int &d, int &y, int &hr, int &min); 
    16:	  void SetTime(int m, int d, int y, int hr, int min); 
    17:	  char *GetSTime(void);                                                  
    18:	  void ChangeTime(long nminutes); 
    19:	}; 
    20: 
    21:	#endif // __TIME1_H 

    Строки 3, 4 и 21 предохраняют заголовочный файл TIME1.H от включения его в 
один модуль более одного раза. (Вы видели аналогичный прием в листинге 11.2 
главы 11.) Объявление класса TTime в заголовочном файле позволяет использовать 
этот класс в других модулях. 
    В листинге 13.4 приводятся функции-члены класса TTime. Подобная 
организация классов в заголовочных файлах и программных модулях типична. 
Следует объявлять один или несколько классов в заголовочном файле, 
заканчивающемся на .Н, и приводить тела функций-членов класса в отдельном 
файле, заканчивающемся на .СРР. Для компиляции этого модуля из DOS введите 
команду Ьсс -с timel.c. Для компиляции его из интегрированной среды сделайте 
текущим окно с TIME.CPP, установите TargetExpert в EasyWin и нажмите <Alt+F9>. 

    ЗАМЕЧАНИЕ 
    Модуль TIME1.CPP остается все еще неполной программой (в нем не хватает 
функции main()). Вы можете скомпилировать TIME1.CPP в объектный файл 
TIME1.OBJ, но для того, чтобы запустить результат на выполнение, вы должны 
написать ешр один модуль, содержащий main(). TIME1.CPP содержит программный 


    Глава 13. Программирование классов                                  311

код, содержащий реализацию класса TTime. Компоновщик может присоединить 
этот модуль (бозможно, вместе с другими) для создания законченной программы. 
Поскольку модуль TTime скомпилирован отдельно, его можно скомпоновать с 
несколькими различными программами. Это делает отдельные модули, подобные 
ТТime, идеальными для сохранения общего программного кода для исяользовавия в 
раэличных приложениях. 

    Листинг 13.4.  TIME.CPP (реализация класса TTime) 

     1:	// timel.cpp -- реализация класса Ttime 
     2: 
     3:	#include <iostream.h> 
     4:	#include <stdio.h> 
     5:	#include <dos.h> 
     6:	#include <string.h>
     7:	#include "timel.h" 
     8: 
     9:	// Отображение даты и времени 
    10:	void TTime::Display(void) 
    11:	{ 
    12:	char s[30]; 
    13:	sprintf(s, "Date: %02d/9402d/%04d Time: %02d:%02d", 
    14:	month, day, year, hour, minute ); 
    15:	cout << s << '\n'; 
    16:	} 
    17: 
    18:	// Возвращает текущие данные-члены дату и время 
    19:	void TTime::GetTime(int &m, int &d, int &y, int &hr, int &min) 
    20:	{ 
    21:	m = month; // Возвращение данных-членов тому, кто вызвал функцию 
    22:	d = day; 
    23:	у = year; 
    24:	hr = hour; 
    25:	min = minute; 
    26:	} 
    27: 
    28:	// Устанавливает данные-члены дату и время
    29:	void TTime::SetTime(int m, int d, int y, int hr, int min) 
    30:	{ 
    31:	month = m; // Присваивание аргументов данным-членам 
    32:	day = d; 
    33:	year = y; 
    34:	hour = hr; 
    35:	minute = min; 
    36:	} 
    37: 
    38:	// Возвращает дату и время в виде строки 
    39:	char *TTime::GetSTime(void) 
    40:	{ 
    41:	char buffer[40]; // Побольше пространства 
    42:	char *cp; // Указатель на результат, возвращаемый функцией 43: 
    44:	sprintf(buffer, "Date: %02d/%02d/%04d Time: %02d:K02d\n", 
    45:	month, day, year, hour, minute ); 
    46:	cp = strdup(buffer); // Копирование с наименьшим возможным размером 
    47:	return cp; 
    48:	}
    50:	// Добавление nminutes (может быть отрицательным) к текущему времени 
    51:	void TTime::ChangeTime(long nminutes) 
    52:	{ 
    53:	struct date ds; 
    54:	struct time ts; 
    55:	long timeinsecs; 
    56: 
    57:	ds.da_year = year; 
    58:	ds.da_mon = month; 
    59:	ds.da_day = day; 
    60:	ts.ti_hour = hour; 
    61:	ts.tijnin = minute; 
    62:	ts.ti_sec = 0; 
    63:	ts.ti_hund = 0; 
    64:	timeinsecs = dostounix(&ds, &ts); 
    65:	timeinsecs += (nminutes * 60); 
    66:	unixtodos(timeinsecs, &ds, &ts); 
    
    
    312	Часть III. Программирование на ANSI C++ 

    67:	year = ds.da_year; 
    68:	month = ds.da_mon; 
    69:	day = ds.da_day; 
    70:	hour = ts.ti_hour; 
    71:	minute = ts.ti min; 
    72:	} 

    Перед изучением того, как в TIME.CPP реализован класс TTime, скомпилируйте 
и запустите листинг 13.5 — образец программы, использующей класс для 
отображения страницы из произвольного календаря встреч. (К счастью для вас, 
ваш календарь не показывает встреч — весь день в вашем распоряжении.) Для 
компиляции программы из DOS введите bcc appointl timel, или, если у вас есть 
уже скомпилированный модуль TIME1, введите bcc appointl timel.obj.

    ЗАМЕЧАНИЕ 
    Чтобы скомпилировать APPOINT.CPP в законченную программу, следует 
объединить все разрозненные модули ТТМЕ1. Команды, приведенные в предыдущем 
параграфе, позволяют сделать это для DOS. Для WINDOWS необходимо из 
интегрированной среды открыть файл проекта APPOINT1.IDE с помощью команд 
Project\Open project... В проекте указываются основной программный модуль 
(APPOINT.CPP), отдельный модуль (TIME1.CPP) и целевой модуль East/Win — 
APPOINT1.EXE Таким же образом следует указать и прочие отдельные модули. После 
открытия файла проекта нажмите <Ctrl+F9> для компиляции всех модулей, 
компоновки получившихся в результате компиляции объектных файлов и запуска на 
выполнение конечного целевого кода. Если у вас получились ошибки, которые 
могут быть вызваны компиляцией модуля TIME1 для DOS или не EasyWin Windows, 
используйте для компиляции Project\Build All. Также установите правильные 
опции local options для APPOINT1.EXE (щелкните правой кнопкой мыши над целевым 
именем), установив верные пути для заголовочных и библиотечных каталогов 
Borland C++. Если неприятности не закончились, перечитайте инструкции к 
компиляции проектов в конце главы 2. 


    Листинг 13.5. APPOINT1.CPP (отображение примера календаря встреч) 

    1:	#include <iostream.h> 
    2:	#include <stdio.h> 
    3:	#include "time1.h" 
    4: 
    5:	main() 
    6:	{ 
    7:	  TTime appointment; 
    8: 
    9:	  appointment.SetTime(7, 21, 1996, 8, 30); 
    10:	  for (int slots = 1; slots <= 17; slots++) { 
    11:	    appointment.Display(); 
    12:	    appointment.ChangeTime(30); 
    13:	  } 
    14:	  return 0; 
    15:	}

    В строке 3 включается TIME1.H, что делает доступным объявление класса 
TTime в программе. В строке 7 объявляется объект класса appointment типа 
TTime. В строке 9 компонентам appointment присваиваются значения даты и 
времени, после чего в цикле for выводится последовательность встреч с разницей 
в 30 мин. В строке 12 вызывается функция-член ChangeTime() для добавления 30 
мин к дате и времени в appointment. В любом случае программа вызывает 
функции-члены для выполнения действий над appointment. 
    Теперь, когда вы увидели, как используется класс TTime, вернемся к 
ТГМЕ1.СРР (листинг 13.4), в котором приводятся функции-члены класса. Кое-что в 
программном коде повторяется из предыдущих листингов. Новые функции-члены — 
GetSTime() (строки 39-48), возвращающая в виде символьной строки дату и время 
объекта TTime, и ChangeTime() (строки 51-72), добавляющая или вычитающая 
указанное количество минут от даты и времени объекта. 
    Эти две новые функции-члены предварены TTime:: для опознания их 
компилятором как членов класса TTime. Функция-член GetSTime() вызывает sprintf() 
(строка 44) для формирования строки с закрытыми данными-членами month, day, 
year, hour и minute, вставленными с соответствующей пунктуацией. Затем в 
строке 46 вызывается strdup() для копирования этой буферной строки в новую 
строку в динамически выделенной области памяти. В строке 47 возвращается 
указатель на строку, которую программа может удалить с помощью вызова функции free(). 
    Функция-член ChangeTime() вызывает две библиотечные функции — dostounix() и 
unixtodos() для преобразования даты и времени объекта класса в формат длинного 
целого и обратно. Таким путем легче всего в 
    
    Глава 13. Программирование классов	313 

строке 65 прибавить или вычесть указанное количество минут, переданных 
функции-члену в качестве параметра функции nminutes. В основной программе 
(листинг 13.5, строка 12) в операторе appointment.ChangeTime(30); вызывается 
функция-член ChangeTime() для сдвига времени в объекте класса appointment на 30 мин. 
    Когда подобный оператор вызывает функцию-член, C++ передает функции адрес 
объекта, в данном случае адрес appointment. Внутри функции-члена адрес объекта 
доступен с помощью специального ключевого слова this. Все функции-члены 
получают указатель this, ссылающийся на объект класса, для которого 
функция-член была вызвана. В листинге 13.5, в строке 12, функции-члену 
ChangeTime() передается указатель this, в котором хранится адрес appointment. 
Иначе говоря, подобным образом функции-члены ссылаются на объекты класса, для 
которых они были вызваны.
    Вы можете использовать this в качестве указателя на объект типа класса. 
Например, строку 21 в листинге 13.4 можно было написать следующим образом: 

    m = this->month; 

    Говоря обычным языком, этот оператор присваивает ссылочному параметру m 
значение month — члена объекта класса, адресуемого this. Конечно, в 
большинстве случаев нет нужды в непосредственном использовании this. Оператор 

    m = month; 

по умолчанию ссылается на month — член объекта класса, на который ссылается this. 

    Изменение закрытых данных-членов 

    Лучшие способы представления данных часто становятся очевидными на полпути 
в сумасшедшем рывке к завершению проекта. Как обсуждалось раньше, сохранение 
даты и времени в виде числа секунд начиная с фиксированной даты (обычно, 1 
января 1970 года в DOS) более удобно и позволяет использовать другие 
библиотечные функции, использующие такой же формат хранения даты и времени. 
    Функция-член ChangeTime() в листинге 13.4 преобразует данные-члены дату и 
время в длинное целое (timemsecs), что расходует время и производительность в 
случае частых вызовов этой функции. Очевидно, эта функция упростится, если 
дата и время уже будут храниться в виде длинного целого. 
    В листингах 13.6 и 13.7 внесены эти изменения в представлении данных в 
классе TTime. Файл TIME2.H и соответствующий ему модуль TIME2.CPP — не 
законченные программы. Их необходимо скомпилировать и скомпоновать вместе с 
еще одним модулем, подобным APPOINT2.CPP, в котором определена функция main(). 
Чтобы скомпилировать результативную программу для DOS, введите команду bcc 
appoint2 time2. Запустите получившийся в результате выполняемый файл 
APPOINT2.EXE, введя его имя. Вы также можете скомпилировать каждый модуль 
отдельно и скомпоновать их объектные файлы с помощью нескольких команд: 

    Ьсс	-с appoint2 
    bcc	-с time2 
    Ьсс	appoints.obj time2.obj 

    Таким образом, существует более чем один способ "спустить шкуру с кошки" и 
скомпилировать программу на C++. Для того чтобы скомпилировать и запустить 
APPOINT2 из интегрированной среды, откройте файл проекта APPOINT2.IDE и 
нажмите <Ctrl+F9>, как это вы уже делали с APPOINT1. Не забудьте использовать 
Project\Build all в случае появления ошибок (конечно, вызванных предыдущей 
компиляцией модуля TIME2 для DOS, что делает объектный код несовместимым с Windows). 

    Листинг 13.6.  TIME2.H (измененный класс TTime) 

   1:  // time2.h -- Объявление класса TTime 
   2: 
   3:  #ifndef __TIME2_H 
   4:  #define __TIME2 H 1 // Предотвращение нескольких #include 
   5: 
   6:  class TTime { 
   7:  private: 
   8:     long dt; // Дата и время - в секундах от 1 января 1970 года 
   9:  public: 
  10:   void Display(void); 
  11:   void GetTime(int &m, int &d, int &y, int &hr, int &min); 
  12:   void SetTime(int m, int d, int y, int hr, int min); 
  13:   char *GetSTime(void); 
  14:   void ChangeTime(long nminutes); 
  15:  } ; 
  16: 
  17: #endif // __TIHE2_H
    
    
    314 201	Часть III. Программирование на ANSI C++ 

    Класс TTime в TIME2.H идентичен TTime в TIME1.H за исключением того, что в 
объявлении присутствует лишь одно закрытое длинное целое dt. Удалены 
индивидуальные компоненты month, day, year, hour и minute. Конечно, несмотря 
на проведенные изменения, открытые функции-члены — те же самые, и нет нужды в 
изменении операторов, в которых используется класс TTime. 
    Собственно функции-члены нуждаются в изменении, но, по крайней мере, эта 
работа ограничена известным числом легко идентифицируемых функций. В листинге 
13.7 решается эта задача. 

    Листинг 13.7.  TIME2.CPP (реализация измененного класса TTime)	: 

    1:    // time2.cpp -- Реализация класса TTime 
    2
    3:	#include <iostream.h>
    4:	#include <time.h>
    5:	#include <dos.h>
    6:	#include <string.h>
    7:	#include "time2.h"
    8: 
    9:    // Отображение даты и времени; 
    10:     void   TTime:: Display( void)) 
    11:     { 
    12:	   cout << ctime(&dt) ; 
    13:     } 
    14: 
    15:	// Возвращает текущие данные-члены (дату и время                       
    16:	void TTine::GetTime(int &m,  int id,   int &y,  int &hr,  int &min)    
    17:	{ 
    18:	  struct date ds; 
    19:	  struct time ts; 
    20: 
    21:	  unixtodos(dt,  &ds,  &ts); 
    22:	  у = ds.da_year; 
    23:	  m = ds.da_mon; 
    24:	  d = ds.da_day; 
    25:       hr = ts.ti_hour; 
    26:       nin = ts.ti_min; 
    27:	} 
    28: 
    29:	// Устанавливает член dt 
    30:	void TTime::SetTime(int m,   int d,  int y,  int hr,  int min) 
    31:	{ 
    32:	struct date ds; 
    33:	struct time ts; 
    34; 
    35:	ds.da_year = у; 
    36:	ds.da_mon = m; 
    37:	ds.da day = d;                                 
    38:	ts.ti_hour = hr;
    39:	ts.ti_min = min; 
    40:	ts.ti_sec = 0; 
    41:	ts.ti hund = 0; 
    42:	dt = dostounix(&ds, &ts); 
    43:	} 
    44: 
    45:	// Возвращает дату и время в виде строки 
    46:	char *TTime::GetSTime(void) 
    47:	{ 
    48:	char *cp = strdup(ctime(&dt));
    49:	return ср; 
    50      } 
    51 
    52:	// Добавление nminutes (может быть отрицательным) к текущему времени
    53:	void TTime::ChangeTime(long nminutes) 
    54:	{ 
    55:	dt += (nminutes * 60); 
    56:	} 

    Новый модуль существенно улучшен по сравнению со старым и занимает меньше 
памяти. Теперь, когда дата  и время сохраняются в виде длинного целого, в 
строке 12 просто вызывается библиотечная функция ctime() для преобразования 
даты и времени в строку ASCII. Функции-члены GetTime() и PutTime() стали 
немyого сложнее, чем до этого, поскольку им теперь необходимо преобразовать 
дату и время в раздельные параметры и обратно с помощью библиотечных функций 
unixtodos() и dostounix(), упоминавшихся ранее. 
    
    13. Программирование классов    -	315

    Функции-члены GetSTime() и ChangeTime()существенно улучшены (строки 45-56). 
GetSTime()передает результат функции ctime()непосредственно strdup() для 
возвращения даты и времени в виде строки. Но лучше всего преобразовалась 
ChangeTime(), выродившись в простой оператор. Поскольку дата и время теперь 
хранятся в нужной форме, нет больше необходимости в операторах преобразования 
из предыдущей версии. 
    Изменение формата данных для упрощения кода — сберегающий время стиль 
оптимизации программ. С помощью ООП и классов C++ изменения фундаментальной 
структуры данных не обязательно потребуют соответствующих изменений 
операторов, использующих эту информацию. Как показано в листинге 13.8, 
основная программа в проекте "не помнит" об основных модификациях, сделанных в 
классе TTime. 
    Новая главная программа. APPOINT2.CPP, идентична APPOINT1.CPP, за 
исключением строки 3, включающей заголовочный файл TIME2.H вместо TIME1.H. В 
этом маленьком примере действительные преимущества ООП практически не заметны. 
Конечно, в программе из 10,000 строк с сотнями объектов класса TTime изменений 
потребуется не больше, чем в этом примере! Инкапсуляция данных и функций в 
классе локализует изменения в коде путем запрещения прямого доступа к данным, 
минимизируя таким образом потенциальное количество модификаций, требующихся 
после изменения внутреннего представления данных. 

    Листинг 13.8. APPOINT2.CPP (отображение примера календаря встреч) 

    1:	#include <iostream.h> 
    2:	#include <time.h> 
    3:	#include "time2 h" 
    4: 
    5:      main() 
    6      { 
    7:	TTime apoointment; 
    8 
    9:      appointment.SetTime(7,  21,  1996, 8,  30); 
  10:      for ( int slots = 1:  slots <= 17; slots++ ) { 
  11:        appointment.Display(): 
  12:        appointment.ChangeTime(30); 
  13:      } 
  14:      return 0; 
  15:    } 


    Встраиваемые функции-члены 

    Итак, вы ознакомились с простыми примерами объявлений классов. C++ 
предлагает много других средств, которые вы можете использовать в классах. 
Например, использование функций-членов для доступа к данным-членам класса 
вырабатывает верное отношение к эффективности. Вызов функции-члена, подобного 
SetTime(), для присваивания значений данным-членам класса занимает больше 
времени, чем простое присваивание значений непосредственно этим членам, 
конечно, если они открыты для доступа. 
    Хотя это наблюдение верно, не забывайте, что одно из основных достоинств 
класса заключается в запрещении доступа к данным, что упрощает поддержку. 
Насколько велика ценность нескольких вызовов функции для стабильности вашей 
программы и смогут ли они упростить внесение в нее изменений? 
    В большинстве случаев вызовы функций-членов вместо прямого доступа к 
данным-членам заметно сказываются на быстродействии программы. Конечно, в наше 
время необходима предельная эффективность. Для этого C++ позволяет объявлять 
классы с встраиваемыми функциями-членами. Хотя встраиваемые функции-члены 
используются точно так же, как и другие функции, в скомпилированном коде они 
не вызываются. Вместо этого встраиваемые функции-члены вставляются 
непосредственно в скомпилированную программу (рис. 13.3). 

    
    Рис. 13.3. Встраиваемые функции-члены вставляются непосредственно в 
программный код 


    316                                 Часть III. Программирование на ANSI C++ 

    Листинг 13.9 заново объявляет класс TTime с использованием нескольких 
встраиваемых функций-членов (строки 14 и 17-22). Как и в предыдущих примерах, 
скомпилируйте и скомпонуйте вместе следующие три листинга для получения работающей 
программы. Находясь в DOS, введите команду bcc appoint3 time3, затем запустите 
получившийся в результате этих действий исполняемый файл APPOINT3.EXE. Или же 
из интегрированной оболочки откройте файл проекта APPOINT3.IDE и нажмите 
<Ctrl+F9> для компиляции и запуска. 

    Листинг 13.9.  (новое объявление класса TTime с встраиваемыми функциями-членами) 

   1:    // time3.h -- Объявление класса TTime 
   2  
   3:    #ifndef __ТIМЕЗ_Н 
   4:    #define __TIME3_H 1 // Предотвращение несколько #include 
   5:    
   6:    #include <iostream.h>                 
   7:    #include <time.h>                             
   8:    #include <string.h>                      
   9: 
  10:    class TTime { 
  11:     private:                                                            
  12:	  	long dt; // Дата и время - в секундах от 1 января 1970 года 
  13:     public: 
  14:	 	void Display(void) { cout << ctime(&dt); } 
  15:	 	void GetTime(int &m, int &d, int &y, int &hr, int &min); 
  16:	 	void SetTime(int m, int d, int y, int hr, int min);                    
  17:	 	char *GetSTime(void) 
  18:		{ 
  19:	  		char *cp = strdup(ctime(&dt)); 
  20:	  		return cp; 
  21:		} 
  22:		void ChangeTime(long nminutes) { dt += (nminutes * 60);  } 
  23:     } ; 
  24: 
  25:   #endif // __ТIME_Н 


    В результате использования встраиваемых функций в TIME3.H реализация 
класса в листинге 13.10 неcколько упрощается. 


    Листинг 13.10.  TIME3.CPP (реализация невстраиваемых функций класса TTime) 

   1:	// timeS.cpp -- Реализация класса TTime 
   2: 
   3:	#include <dos.h>                                                       
   4:	#include "time3.h" 
   5:	
   6:	// Возвращает текущие данные-члены дату и время                  
   7:	void TTime::GetTime(int &m, int &d, int &y, int &hr, int &min) 
   8:	{                                                : 
   9:		struct date ds;                                
  10:		struct time ts;                                      
  11:
  12:		unixtodos(dt, &ds. &ts);                 
  13:		y = ds.da_year; 
  14:		m = ds.da_mon; 
  15:		d = ds.da_day; 
  16:		hr = ts.ti_hour;                                                      
  17:       min = ts.ti_min;	
  18:  }
  19: 
  20:	// Устанавливает член dt 
  21:	void TTime::SetTime(int m,  int d,  int y,  int ir, int min)
  22:	{ 
  23:		struct date ds;                                                        
  24:		struct time ts;                                                        
  25: 
  26:		ds.da_year = y; 
  27:		ds.da_mon = m; 
  28:		ds.da_day = d; 
  29:		ts.ti_hour = hr; 
  30:		ts.ti_min = min;                                                       
  31:		ts.ti_sec = 0; 
  32:		ts.ti_hund = 0; 
  33:		dt = dostounix(&ds, &ts); 
  34:	} 
    

    Глава 13. Программирование классов	317 

    Поскольку использование встраиваемых функций-членов не отличается от 
использования обычных функций-членов, основная программа осталась без 
изменений. Листинг 13.11 идентичен APPOINT2.CPP за исключением того, что в 
строке 3 теперь включается заголовочный файл TIME3.H вместо TIME2.H. 
Скомпилируйте программу полностью, введя в DOS команду bcc appoint3 time3. Из 
интегрированной среды вы можете создать и скомпилировать проект, состоящий из 
файлов APPOINT3.CPP и TIME3.CPP. 

    Листинг 13.11.  APPOINT3.CPP (отображение примера календаря встреч) 

    1:	#include <iostream.h> 
    2:	#include <stdio.h> 
    3:	#include "time3.h" 
    4:	 
    5:	main() 
    6:	{ 
    7:	TTime appointment; 
    8:	 
    9:	appointment.SetTime(7, 21, 1996, 8, 30); 
    10:	for (int slots = 1; slots <= 17; slots++ ) { 
    11:	appointment.Display() ; 
    12:	appointment.ChangeTime(30) ; 
    13:	
    14	return 0; 
    15:	}
      __________________________________________________ 


    В строке 14 в Т1МЕЗ.Н (листинг 13.9) приводится объявление типичной 
встраиваемой функции-члена. Для ссылки приведем сокращенную версию объявления 
класса TTime: 

    class TTime { 
	private: 
    		long dt; 
	public: 
	    void Display(void)  { cout << ctime(&dt);   } 
    } 

    Тело функции-члена Display() заключено в фигурные скобки сразу после 
заголовка функции в объявлении класса. Обратите внимание на положение точки с 
запятой, завершающей оператор вывода в поток cout. Такой формат логичен для 
правил С и C++, которые гласят, что точка с запятой завершает объявления и 
операторы. Вы можете найти, что этот формат удобнее читать, если написать 
встраиваемую функцию-член, используя стиль с отступами: 

  class TTime { 
   private: 
     long dt; 
   public: 
     void Display(void) 
     { 
       cout « ctirae(idt); 
     } 
  }; 

    Оба формата работают одинаково хорошо, но для экономии пространства 
большинство встраиваемых функций написаны в одну-две строки, как было показано 
раньше. Операторы, в которых происходит вызов функции Display(), не генерируют 
вызов функции в скомпилированном коде. Например, вы объявили объект класса и 
установили в нем дату и время: 

    TTime today; 
    today.SetTime(4,   19,   1998,   6,  0); 

    Затем вы можете вызвать функцию-член Display() для объекта today:

    today.Display();	

    Несмотря на внешний вид, программа с компилируется так, как если бы вы написали 

    cout << ctime(&today.dt); 

    Конечно, вы не можете в действительности написать такой оператор, 
поскольку dt — закрытый член класса TTime и, следовательно, недоступен вне класса. 

    ЗАМЕЧАНИЕ 
    Как и в случае с обычными встраиваемыми функциями, C++ может и не вставить 
функцию-член непосредственно в скомпилированный код. Например, если 
встраиваемая функция-член слишком сложна, компилятор может решить 
скомпилировать ее как обычную функцию член. 
    

    318	Часть III. Программирование на ANSI C++ 

    Большинство функций-членов объявляются непосредственно в классе, как это 
показано в строке 14 листинга 13.9. Вы также можете объявить встраиваемую 
функцию-член с помощью ключевого слова inline в модуле реализации класса. 
Например, для преобразования GetTime() из TTime во встраиваемую функцию, вы 
можете изменить реализацию функции в листинге 13.10 следующим образом: 

    inline void TTime::GetTime(in &m,   int &y,  int &hr,   int &mip) 
    {
      ....
    }

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

    Перегруженные функции-члены 

    В главе 12 вы узнали, как перегружать функции, объявляя несколько функций 
с одним и тем же именем, но отличающихся, по крайней мере, одним параметром. 
Вы можете использовать аналогичный трюк и для перегрузки функций-членов класса. 
    Перегруженные функции-члены особенно полезны, когда необходимо несколько 
параметров (а не все) функции-члена. Например, в текущей реализации функции 
SetTime() класса TTime необходимо пять параметров. Для того чтобы установить 
дату и время: 15 января 1997 года, 11:45, вы должны написать 

    TTime anyTime; 
    anyTime.SetTime( 1,   15,   1997,   11,   45 ); 

    Предположим, у вас возникло естественное желание установить только дату, 
при этом время класса по умолчанию равно текущему. На данный момент 
инициализировать подобным образом TTime не так-то просто. Перегрузка SetTime()
с различным числом параметров дает возможность в программе передавать 
произвольное число аргументов, как в следующем выражении 

    anyTime.SetTime( 1,   15,   1997); 

    В листинге 13.12 приводится новый класс TTime и несколько перегруженных 
функций-членов SetTime(). В заголовке TIME4.H содержатся только объявления и, 
следовательно, их нельзя скомпилировать. Следующие два листинга включают 
TIME4.H с его объявлением класса. 

    Листинг 13.12.  TIME4.H (класс TTime с перегруженными функциями-членами) 

    1:	// time4.h -- Объявление класса TTime                                   
    2:	
    3:	#ifndef __TIME4_H 
    4:	#define __TIME4 H 1 // Предотвращение нескольких #include
    5:	 
    6:	#include <iostream. h>    
    7:	#include <time.h>       
    8:	#include <string.h>                
    9:	
    10:	class TTime {
    11:	private:                
    12:	  long dt; // Дата и время - в секундах от 1 января 1970 года          
    13:	public: 
    14:	  void Display(void) { cout << ctime(&dt); } 
    15;	  void GetTime(int &im, int &id, int &y, int &hr, int &min); 
    16:	  void SetTime( int m, int d, int y, int hr, int min); 
    17:	  void SetTime( int m, int d, int y, int hr); 
    18:	  void SetTime( int m, int d, int y); 
    19:	  void SetTitne( int m, int d);                   
    20:	  void SetTime( int m);            
    21:	  void SetTime(void); 
    22:	  char *GetSTime(void) 
    23:	  { 
    24:	   char *cp = strdup(ctime(&dt)); 
    25:	   return cp;                                                             
    26:	  } 
    27:	  void ChangeTime(long nminutes) { dt += (nminutes * 60); } 
    28:    }; 
    29: 
    30:  #endif // __TIME4_H
     ______________________________________________________ 

    В строках 16-21 объявляется шесть перегруженных функций-членов SetTime().
Все они имеют одинаковое имя и отличаются хотя бы одним параметром — таково 
минимальное требование, чтобы компилятор мог их 

    
    Глава 13. Программирование классов,	319 

различить. И хотя все эти функции-члены имеют одинаковое имя, тем не менее 
все они различны, и их тела должны быть реализованы раздельно, как это 
показано в листинге 13.13. 
    В модуле TIME4.CPP не реализована функция main(), и, как и любой другой 
отдельный модуль, он может быть скомпилирован. Но для того чтобы запустить на 
выполнение результат, необходимо скомпоновать его с модулем, содержащим 
основную программу, как это описывалось для предыдущих листингов. 

    Листинг 13.13.  TIME4.CPP (реализация перегруженных функций-членов) 
    
    1:	// time4.cpp -- Реализация класса TTime 
    2: 
    3:	#include <dos.h> 
    4:	#include "time4.h" 
    5: 
    6:	// Возвращает текущие данные-члены дату и время 
    7:	void TTime::GetTime(int &m, int &d, int &y, int &hr, int &min) 
    8:	{ 
    9:	struct date ds; 
    10:	struct time ts; 11: 
    12:	unixtodos(dt, &ds, its); 
    13:	у = ds.da_year;            
    14:	m = ds.dajnon; 
    15:	d = ds.da_day; 
    16:	hr = ts.ti_hour;                                           
    17:	min = ts.tijnin; 
    18:	} 
    19: 
    20:	// Устанавливает член dt 
    21:	void TTime::SetTime(int m, int d, int y, int hr, int min) 
    22:	{ 
    23:	struct date ds; 
    24:	struct time ts; 
    25:	
    26:	getdate(&ds); // Получение текущих даты и времени 
    27:	gettime(&ts); 
    28:	if (у >= 0) ds.da_year = у; 
    29:	if (m >= 0) ds.da_mon = m; 
    30:	if (d >= 0) ds.da_day = d; 
    31:	if (hr >= 0) ts.ti_hour = hr; 
    32:	if (min >= 0) ts.ti_min = min; 
    33:	ts.ti_sec = 0;         
    34:	ts.ti_hund = 0;                 
    35:	dt = dostounix(ids, its);  : 
    36:	}                   
    37: 
    38:	void TTime::SetTime(int m, int d, int y, int hr) 
    39:	{ 
    40:	SetTime(m, d, y, hr, -1);  
    41:	} 
    42: 
    43:	void TTime::SetTime(int m, int d, int y)  
    44:	{ 
    45:	SetTime(m, d, y, -1. -1); 
    46:	} 
    47: 
    48:	void TTime::SetTime(int m,   int d) 
    49:	{                                                                      
    50:	SetTime(m,  d,  -1,   -1,   -1); 
    51:	} 
    52: 
    53:	void TTime::SetTime(int m)    = 
    54:	{ 
    55:	SetTime(n,   -1,   -1,   -1,   -1); 
    56:	} 
    57:	
    58:	void TTime::SetTime(void) 
    59:	{
    60;	 SetTime(-1,   -1,   -1,   -1,   -1);                                   
    61:	}
    ___________________________________________________ 

    Новая, полностью оснащенная функция-член SetTime() в строках 1-36 вызывает 
библиотечные функции getdate() и gettime() для получения текущих системных даты 
и времени. В этой новой версии SetTime() 
    
    320	Часть III. Программирование на ANSI C++ 

данвым-членам объекта класса присваиваются значения любого числа аргументов — 
от нуля и более, не затрагивая при этом текущих значений по умолчанию для тех 
параметров, которые не указаны. Если today - объект, класса ТТуре, оператор

   today.SetTime( O,  15,  1998,  -1,  -1);          

устанавливает дату, хранящуюся в today, равной 15 января 1998 
года, но с текущим временем, поскольку эти два аргумента установлены равными -1. 
    Поскольку функция перегружена, теперь нет нужды вводить два отрицательных 
аргумента, занимающих лишнее место. Вы можете просто написать 

    today.SetTime(1, 15, 1998);

    В этом случае вызывается перегруженная функция-член с тремя целочисленными 
параметрами (строки 43-46), жоторая снабжает недостающие аргументы 
отрицательными значениями. Компилятор знает, какая из перегруженных 
функций-членов SetTime() вызывается, основываясь на числе и типе аргументов, 
использованных в операторе. Прочие перегруженные функции-члены реализованы 
подобным же образом в терминах полностью оснащенной SetTime() с пятью 
параметрами. Например, оператор 

    today.SetTime() ;	

вызывает перегруженную функцию-член SetTime(), реализованную в строках 
58-61 и не имеющую аргументов, которая, в свою очередь, вызывает версию с 
пятью аргументами для установки в today системной даты и времени. 
    В листинге 13.14 демонстрируется использование перегруженных 
функций-членов TTime. Скомпилируйте программу и модуль поддержки TIME4.H из 
DOS, введя команду bcc overmf time4. Запустите получившийся в результате 
выполняемый файл OVERMF.EXE, введя его имя. Или же из интегрированной оболочки 
с помощью меню Project откройте файл проекта OVERMF. IDE и затем нажмите 
<Ctrl+F9> для компиляции и запуска.

    Листинг 13.14.  OVERMF.CPP (использование перегруженных функций-членов)

    1:	#include	<iostream.h> 
    2:	#include	<stdio.h> 
    3:	#include	"time4.h"
    4:	
    5:	main()
    6.      {
    7:           TTime appointment;
    9:	appointment.SetTime();
    10:	appointment.Display();            
    11:	appointmentSetTime(8); 
    12:	appointment.Display(); 
    13:	appointment.SetTime(8, 1);
    14:	appointment.Display();            
    15:	appointment.SetTime(8, 1, 1996);     
    16:	appointment.Display(); 
    17:	appointment.SetTime(8, 1, 1996, 8); 
    18:	appointment.Display(); 
    19:	appointment.SetTime(8, 1, 1996, 8, 30); 
    20:	appointment.Display(); 
    21:	return 0; 
    22:      } 

    В строках 9-20 вызывается каждая перегруженная функция-член SetTime(), с 
передачей от нуля до пяти аргументов, а также вызывается Display(). При запуске 
программы чередующиеся вызовы Display() отобразят результат каждого обращения к SetTime(). 

    Параметры функций-членов по умолчанию 

    Многочисленные перегруженные функции-члены, подобные приведенным в строках 
16-21 листинга 13.12, полезны, но зачастую приводят к путанице в коде. В 
некоторых случаях параметры функций-членов по умолчанию помогают сократить 
количество функций-членов, необходимых для поддержки вызовов с различным 
числом аргументов. 
    В главе 12 объяснялось использование параметров по умолчанию для обычных 
функций. Параметры функций-членов по умолчанию в точности такие же, как и те, 
которые вы уже изучили, только они используются в объявлении классов. В 
листинге 13.15, приводится новая версия функции SetTime() класса TTime, 
использующая параметры по умолчанию. Как и другие заголовочные файлы, TIME5.H 
содержит только объявления, и его нельзя компилировать. Но вы можете 
скомпилировать другие модули, включающие в себя этот заголовочный файл, для 
того, чтобы использовать класс TTime. 

    Глава 13. Программирование классов	321 
    11  Освоение Borland® C++4.5 


    Листинг 13.15. TIME5.H (новая версия класса TTime с использованием 
параметров функций-членов по умолчанию) 

    1: // time5.h -- объявление класса TTime 
    2:   #ifndef __TIME5_H 
    3: 
    4:   #define __TIME5_H 1 // Предотвращение нескольких #include 
    5:   #include <iostream.h> 
    6:   #include <time.h> 
    7:   #include <string.h> 
    8:
    9:
    10:	class TTime {
    11:	private:	
    12:	  long dt; // Дата и время - в секундах от	1 января 1970 года 
    13:	public: 
    14:       void Display(void) { cout << ctime(&dt) }
    15:	  void GetTime( int &m, int &d, int &y, int &hr, int imin ) ; 
    16:	  void SetTime(int m =	-1, int d =	-1, int У = -1, 
    17:	                           int hr = -1, int min = -1 ) ; 
    18:	  char *GetSTime(void) 
    19:	  {
    20:	    char *cp = strdup(ctime( &dt ) );			
    21:	    return cp;
    22:	  }
    23:	  void ChangeTime( long nminutes ) { dt += (nminutes * 60) ;  }              
    24:      }; 
    25: 
    26:     #endif // __TIME5_H 
    
    Каждому из параметров SetTime() в строках 16-17 присваивается значение по 
умолчанию -1, использовавшееся для идентификации неуказанных параметров в 
вызове этой функции-члена. Значения параметров по умолчанию должны следовать за 
всеми прочими параметрами в объявлении функции-члена. В данном объекте класса 
TTime с именем today оператор 
    
    today.SetTime(15,1998);	

передает дату 15 января 1998 года первым трем параметрам SetTime(). Поскольку 
неуказанные аргументы принимают значения по умолчанию, последние два аргумента 
неявным образом устанавливаются равными -1, и предыдущий оператор компилируется 
так, как если бы он был написан                                             

    today.SetTime(1, 15, 1998, -1, -1); 

    Новая реализация класса TTime значительно проще в сравнении с перегрузкой 
функций-членов SetTime(). В листинге 13.16 приводится результат. Файл TIME5.CPP 
— отдельный модуль, вы можете его скомпилировать, но не можете запустить его 
на выполнение. 
    Значения функций-членов по умолчанию не приводятся в заголовке функции в 
строке 21. Значения по умолчанию могут присутствовать только в объявлении 
функции-члена. 
    В листинге 13.17 используется новый класс TTime. Демонстрируется, что в 
этом случае параметры функций-членов по умолчанию позволяют использовать 
переменное число аргументов в вызовах функции SetTime(). Конечный результат 
такой же, как и в приведенном ранее примере перегрузки, но реализация класса 
стала намного проще. Скомпилируйте и скомпонуйте программу вместе с модулем 
поддержки TIME5, введя команду DOS bcc default time5. Или же скомпилируйте и 
запустите программу как Easy Win-приложение, открыв с помощью меню Project 
файл проекта DEFAULT. IDE и нажав <Ctrl+F9>. 

    Листинг 13.16.  TIME5.CPP (реализация упрощенного класса TTime) 

    1:   // time5.cpp — Реализация класса TTime 
    2: 
    3:      #include <dos.h> 
    4:      #include "time5.h" 
    5:
    6:	// Возвращает текущие данные-члены дату и время 
    7:	void TTime::GetTime( int &m, int &d, int &y, int &hr, int &min) 
    8:	{ 
    9:	  struct date ds; 
    10:	  struct time ts; 11: 
    12:	  unixtodos(dt, &ds, &ts); 
    13:	  у = ds.da_year; 
    14:	  m = ds.da_mon;                                 
    15:	  d = ds.da_day; 


    322      Часть III. Программирование на ANSI C++ 


    16:       hr = ts.ti_hourr;  
    17:	  min = ts.ti_min;
    18:      }
    19:
    20:      // Устанавливает член dt                                      
    21:      void TTime::SetTime(int m, int d, int y, int hr, int min) 
    22:      {
    23:	  struct date ds; 
    24:	  struct time ts;                  
    25:
    26:	 getdate(Sds);  // Получение текущих даты и времени 
    27:	 gettirae(&ts); 
    28:	 if (у >= 0) ds.da_year = у; 
    29:	 if (m >= 0) ds.da_mon = m; 
    30:	 if (d >= 0) ds.da_day = d; 
    31:	 if (hr >= 0) ts.ti_hour = hr; 
    32:	 if (min >= 0) ts.ti_nin = min; 
    33:	 ts.ti_sec = 0; 
    34:	 ts.ti_hund = 0; 
    35:	 dt = dostounix(&ds,  &ts); 
    36:	} 
    
    Листинг 13.17.  DEFAULT.CPP (использование параметров функций-членов по умолчанию) 

    1:	#include <iostream.h>               
    2:	#include <stdio.h>     
    3:	#include "times. h"	
    4:		
    5: 
    6:	main()          
    7: 
    8.	TTime appointment;   
    9:	appointment.SetTime() ; 
    10:	appointment.Display() ; 
    11:	appointment.SetTime(8);	
    12:	appointment.Display() ;	
    13:	appointment.SetTime(8, 1); 
    14:	appointment.Display() ; 
    15:	appointment.SetTime(8, 1, 1996); 
    16:	appointment.Display() ; 
    17:	appointment.SetTime(8, 1, 1996, 8); 
    18:	appointment.Display();	
    19:	appointment.SetTiue(8, 1. 1996, 8, 30);	
    20:	appointment.Display() ;	
    21:	return 0;	
    22:	} 


    Конструкторы и деструкторы 

    До сих пор инициализация объекта класса происходила при его создании. 
Возьмем любой из предыдущих классов TTime. Объявление	

    TTime anyTime ;    
    
создает объект класса anyTime типа TTime, но не инициализирует его 
данные-члены. Если вы забыли инициализировать объект класса, оператор 

   anyTime.Display(); 

скорее всего выведет всякий мусор или, по крайней мере, неверную дату и время. 
    В классах может быть объявлено несколько конструкторов для автоматической 
инициализации объекта класса при его создании, практически исключая таким 
образом возможность ошибок, вызванных использованием неинициализированных 
данных. В классах также может быть объявлен (но только один) деструктор, 
вызываемый для очистки при выходе объекта класса из области видимости. 
Конструкторы и деструкторы похожи на обычные функции-члены, но они реже 
вызываются непосредственно в операторах программы. C++ автоматически вызывает 
конструкторы и деструкторы для инициализации и очистки объектов класса. 
    В листинге 13.18 объявляется еще одна версия класса TTime. Новый класс 
имеет два конструктора для инициализации заново созданных объектов класса TTime. 
В нем также имеется деструктор для очистки старых объектов класса TTime, выходящих 
из области видимости. Это только заголовочный файл. Вы не можете его скомпилировать. 

   Глава 13. Программирование классов      323 


    Листинг 13.18. TIME6.H (объявление класса TTime с конструкторами и деструктором) 

    1:   // time6.h -- Объявление класса TTime 2: 
    3:   #ifndef __TIME6_H 
    4:   #define __TIME6_H 1 // Предотвращение нескольких «include 5: 
    6:   #include <iostream.h>	
    7:   #include <time.h>           
    8:   #include <string.h> 
    9:
    10:	class TTime {
    11:	private:
    12:	   long dt; // Дата и время - в секундах от 1 января 1970 года 
    13:	   char *dts; // Представление	даты и времени в виде строки 
    14:	   void DeleteDts(void); // Удаляет указатель tits 
    15:	public: 
    16:	  TTime() ; // Конструктор 
    17:	  TTime( int m, int d = -1, int y =	-1,	//	Конструктор 
    18:		int hr = -1, int min = -1) ; 
    19:	  ~TTime(); // Деструктор 
    20:	  void Display(void) { cout «	ctime(Sdt);			}
    21:	  void GetTime( int &m, int &d, int &y, int &hr, int &min ) ; 
    22:  	  void SetTime(int m = -1, int d =	-1, int y = -1,
    23:	 	       int hr = -1, int min = -1);				
    24:		const char *GetSTime(void);				
    25:	 void ChangeTime(long nminutes)                                         
    26:	{ dt += (nminutes * 60); DeleteDts(); } 
    27:	}
    28: 
    29:	#endif // __TIHE6_H 

    Конструкторы объявлены как функции-члены, не возвращающие никаких 
значений, с произвольным числом параметров любого типа (за исключением 
классов, которым принадлежат эти конструкторы). Конструкторы, как правило, 
объявляются в открытой секции, но могут определяться в любом месте класса. Они 
всегда имеют такое же имя, как и класс, для которого объявляются. Они не 
всегда могут быть скомпилированы встраиваемыми. 
    В строке 16 объявляется TTime() в качестве конструктора класса TTime. 
Поскольку конструктор объявляется без параметров, он вызывается по умолчанию. 
В строках 17-18 объявляется второй перегруженный конструктор для TTime. Этот 
конструктор также имеет одинаковое с классом имя, но на этот раз он объявлен с 
пятью целочисленными параметрами, из которых последние четыре передают 
значения по умолчанию -1. В TTime также объявлен в строке 19 деструктор, но о 
нем речь пойдет позже. 
    В дополнение к конструкторам и деструктору новый класс TTime имеет еще два 
отличия. В строке 13 добавлен второй закрытый член класса — символьный 
указатель dts. Этот указатель содержит адрес строкового представления даты и 
времени. Как вы увидите позже, хранение даты и времени в виде символьной 
строки помогает облегчить управление памятью в классе, а также может улучшить 
быстродействие программы. В строке 14 добавлена закрытая функция-член 
DeleteDts(), освобождающая память, на которую указывает dts. Закрытые 
функции-члены могут вызываться только другими функциями-членами класса и, как 
и другие закрытые члены, недоступны вне класса. Невозможно в произвольном 
операторе программы вызвать DeleteDts() и освободить память, на которую 
ссылается dts. Только члены класса имеют подобную привилегию. 
    Перед рассмотрением примера использования конструктора, изучим реализацию 
TTime в листинге 13.19. Как и в предыдущей версии класса TTime, TIME6.CPP — 
отдельный модуль, в котором отсутствует функция main(), следовательно, вы 
можете скомпилировать его, но не сможете запустить на выполнение. Далее будет 
приведен листинг, использующий модули объявления класса и его реализации. 

    Листинг 13.19.  TIME6.CPP (реализация TTime)

    1:   // time6.cpp -- реализация класса TTime 
    2: 
    3:   #include <dos.h>	
    4:   #include "time6.h" 
    5: 
    6:   // Конструктор по умолчанию 
    7:   TTime::TTime() 
    8:   { 
    9:    dts = NULL; // Обнуление текущей строки 
  10:     SetTime(-1, -1, -1, -1, -1); 
  11:    } 
  12:	
    

    324	Часть III. Программирование на ANSI C++ 


  13:	// Перегруженный конструктор 
  14:	TTime::TTime( int m, int d, int y, int hr, int min )
  15:	{ 
  16:	  dts = NULL; // Обнуление текущей строки 
  17:	  SetTime(m, d, у, hr, min); 
  18:	} 
  19: 
  20:	// Деструктор        
  21:	TTime::~TTime()
  22:	{ 
  23:	  delete dts; // Удаление строки, хранящейся в объекте 
  24:	} 
  25: 
  26:	// Удаление указателя dts             
  27:	void TTime::DeleteDts(void) 
  28:	{ 
  29:	  delete dts; // Удаление строки, хранящейся в объекте 
  30:	  dts = NULL; // Обнуление указателя 
  31:	} 
  32: 
  33:	// Возвращает текущие данные-члены дату и время 
  34:	void TTime::GetTime(int &m, int &d, int &y, int &hr, int &min) 
  35:	{ 
  36:	  struct date ds;                
  37:	  struct time ts;                
  38: 
  39:	  unixtodos(dt, &ds, its); 
  40:	  у = ds.da_year; 
  41:   m = ds.da_non; 
  42:	  d = -ds.da_day; 
  43;	  hr = ts.ti_hour; 
  44:	  min = ts.ti_min; 
  45:	} 
  46: 
  47:	// Устанавливает член dt 
  48:	void TTime::SetTime(int m,  int d,  int y,  int hr,  int min) 
  49:	{ 
  50:	  struct date ds; 
  51:	  struct time ts; 
  52: 
  53:	  getdate(&ds);   // Взятие текущих даты и времени             
  54:	  gettime(&ts); 
  55:	  if (у >= 0) ds.da_year = у ;                                        
  56:	  if (m >= 0) ds.da_mon = m ;                                    
  57:	  if (d >= 0) ds.da_day = d ; 
  55:	  if  (hr >= 0) ts.ti_hour = hr ;             
  59:	  if (mm >= 0) ts.ti_min = min;                  
  50:	  ts.ti_sec = 0 ;
  61:	  ts.ti_hund = 0;                                                 
  62:	  dt = dostounix(&ds, &ts);                                         
  63:	  DeleteDts();  // Удаление текущей строки 
  64: }                                                           
  65: 
  66:	const char *TTime::GetSTime(void) 
  67:	{                                                                      
  68:	  if (dts)  // Возвращает текущую строку,  если она инициализирована 
  69:	    return dts; 
  70:	  dts = strdup(ctime(&dt));                                              
  71:	  return dts;                                                            
  72:	} 

    В строках 7-11 приводится конструктор TTime по умолчанию — TTime(). Как и в 
случае всех прочих функций-членов, заголовку реализации предшествует имя 
класса и оператор разрешения области видимости. Хотя внешний вид заголовка 
TTime::TTime(), кажется, страдает от зеркального отражения, тем не менее он 
верно идентифицирует конструктор TTime(), как принадлежащий классу TTime (рис. 13.4). 
    Программирование внутренних операторов конструктора ничем не отличается от 
программирования других функций-членов или обычных функций. Конечно, 
обязанности типичного конструктора ограничены присваиванием начальных 
значений данным-членам класса, выделением памяти, используемой объектом 
класса, и т.д. В данном случае в строке 16 символьный указатель dts 
устанавливается равным NULL, это значит, что указатель еще не ссылается на 
строку. Поскольку C++ автоматически вызывает конструктор для инициализации 
объекта класса TTime, следовательно, все такие объекты гарантированно имеют 
инициализированный указа-
    
    Глава 13. Программирование классов	325 

тель dts. Конструктор также вызывает функцию-член SetTime() с аргументами 
по умолчанию -1 (строка 10), устанавливая таким образом в объекте класса 
текущие дату и время. 

    Рис. 13.4. Детали и части конструктора 

    Второй перегруженный конструктор приводится в строках 14-18. Он похож на 
конструктор по умолчанию, но объявлен с параметрами даты и времени и может 
использоваться для инициализации значений объекта класса. Перегруженные 
конструкторы похожи на перегруженные функции-члены. В любом классе вы можете 
объявить столько конструкторов, сколько потребуется, при условии, что они 
отличаются хотя бы одним своим параметром. Конструкторы должны иметь 
одинаковое имя с классом, в котором они определены и, следовательно, 
перегружаются по определению. 

    Использование конструкторов 

    Не все классы нуждаются в конструкторах, но в большинстве из них определен 
хотя бы один. В иных классах может быть дюжина конструкторов и даже более. 
Цель конструкторов — обеспечить ясно определенную инициализацию объектов 
класса. В листинге 13.20 демонстрируется инициализация объектов класса TTime 
конструкторами TTime(). Скомпилируйте программу и модуль поддержки TIME6 из 
командной строки DOS, введя bcc construc time6. Запустите на выполнение 
получившийся в результате выполняемый файл CONSTRUC.EXE путем ввода его имени. 
Или из интегрированной среды откройте проектный файл CONSTRUC.IDE с помощью 
меню Project и нажмите <Ctrl+F9> для компиляции и запуска программы в качестве 
приложения EasyWin. 

    Листинг 13.20.   CONSTRUC.CPP (использование конструкторов TTime) 

    1:	#include <iostream. h> 
    2:	#include <stdio.h> 
    3:	#include "time6.h"    
    4: 
    5:	main() 
    6:	{ 
    7:	 TTime t1; 
    8:	 TTime t2(8); 
    9:	 TTime t3(8,   1);
  10:	 	 TTime t4(8,   1, 1996);     
  11:	 	 TTime t5(8,   1, 1996,   8 ) ; 
  12:	 	 TTime t6(8,   1, 1996,  8, 30) ;
  13:  
  14:	  	 t1.Display(); 
  15:	 	 t2.Display();		
  16: 	 t3.Display();
  17: 	 t4.0isplay();
  18: 	 t5.Display();
  19:        t6.Display();	
  20:	 	 return 0 ; 
  21:		} 

    В строках 7-12 объявляются шесть объектов класса TTime. C++ автоматически 
вызывает конструктор класса при его создании. Например, когда программа 
выделяет пространство для tl, как это объявлено d строке 7, вызывается 
конструктор по умолчанию для инициализации объекта. 
    В строках 8-12 объявляются и инициализируются объекты класса, начиная с t2 
и заканчивая t6, с указанием явных параметров в круглых скобках. Эти объявления 
имеют сходство с более ранними примерами, в которых объявлялся объект класса 
TTime и затем вызывались функции-члены SetTime() для присваивания 

    326     Часть III. Программирование на ANSI С++

значений данным-членам объекта. Конечно, с использованием конструкторов 
вся эта работа автоматически выполняется во время создания объектов. С помощью 
конструкторов объекты класса гарантированно инициализируются, что помогает 
предупредить одну из самых распространенных ошибок в программировании — 
использование неинициализированных переменных. 

    СОВЕТ
    Попробуйте с помощью встроенного отладчика исследовать, как в приведенном 
образце программы вызываются различные конструкторы TTime. Сначала с помощью 
меню Project откройте CONSTRUC.IDE, затем выберите CONSTRUC.CPP в окне проекта 
для того, чтобы открыть этот файл. Поместите курсор на первый оператор main() и 
нажмите <F5> для установки точки прерывания на этой строке. Установите также 
точки прерывания на следующих пяти строках. Все шесть строк должны выделяется 
на цветном мониторе красным цветом. Нажмите <Ctrl+F9> для компиляции и запуска 
программы. Выполнение прервется на первой точке прерывания. Для того чтобы 
определить, какой конструктор вызывается в операторе, нажмите <F9> для входа в 
тело конструктора. Это приведет вас к конструктору по умолчанию. Нажмите 
<Ctrl+F9> лдя продолжения выполнения программы до следующей точки останова и 
затем опять нажмите <F7> для входа в вызов функции, на этот раз вы попадете во 
второй конструктор класса. Повторяйте эти действия до тех пор, пока программа 
не начнет отображение.

    Использование деструкторов 

    Еще один пример программы демонстрирует использование класса TTime из 
TIME6.H и TIME6.CPP (листинги 13.18 и 13.19), а также показывает, как 
используется деструктор для очистки объекта класса при его выходе из области 
видимости. Скомпилируйте листинг 13.21, введя в DOS команду bcc destruc timeG 
или из интегрированной оболочки откройте с помощью меню Project проектный файл 
DESTRUC.IDE; нажмите <Ctrl+F9> для компиляции и запуска программы как 
приложения Easy Win. Поскольку DESTRUC использует модуль TIME6 из предыдущего 
листинга и вы могли скомпилировать TIME6 ранее для DOS, вам, может быть, 
придется применить команду Project\Build all для компиляции этой программы 

    Листинг 13.21.   DESTRUC.CPP (использование деструктора TTime) 

    1:   #include <iostream.h> 
    2:   #include <stdio.h> 
    3:   #include "time6.h" 
    4: 
    5:   void f(void); 
    6: 
    7:   main() 
    8:    { 
    9:       f(); 
   10:     return 0; 
   11:    } 
   12: 
   13:   void f(void) 
   14:    { 
   15:     TTime today; 
   16:     const char *sp; 
   17: 
   18:     sp = today.GetSTime(); 
   19:     cout << "First time:   " << sp ; 
   20:     sp = today.GetSTime(); 
   21:     cout << "Second time:   " << sp; 
   22:    } 
    
    Функция main() в DESTRUC.CPP вызывает другую функцию f(), приведенную в 
строках 13-22. Внутри функции в строке 15 объявляется автоматический объект 
класса TTime today, локальный в области видимости функции. Поскольку локальный 
автоматический объект создается вместе с вызовом функции, в которой он 
объявлен, C++ вызывает для today конструктор Ttime() по умолчанию в качестве 
части кода, выполняемого функцией при запуске. Подобная операция выполняется 
всякий раз при вызове функции. (Вы можете проверить этот факт из 
интегрированной среды с помощью встроенного отладчика.) 
    В строках 18-21 вызывается функция-член GetSTime() для объекта класса 
today. Результат этой функции сохраняется в локальном символьном указателе sp. 
Вернемся к листингу 13.19, в котором приводится реализация GetSTime() (строки 
66-72). Функция-член сначала проверяет, не содержит ли член dts нуль. Если 
нет, то возвращается dts, иначе вызываются strdup() и ctime() для преобразования 
текущей даты и времени в символьную строку, с сохранением адреса строки в 
символьном указателе dts — закрытом члене класса. Таким 

    Глава 13. Программирование классов     327 

образом, последующие вызовы GetSTime() будут использовать созданную ранее 
строку. Это сокращает количество выделений пространства в динамически 
распределяемой области памяти для строкового представления даты и времени, 
уменьшает фрагментацию динамически распределяемой области и улучшает 
быстродействие программы. 
    Поскольку объект класса TTime владеет указателем dts, ссылающимся на 
выделенный блок памяти, класс должен аккуратно управлять распределением этой 
памяти. Объект типа TTime не должен выходить из области видимости без 
освобождения выделенной этому объекту памяти, иначе это приведет к ошибкам, 
связанным с нехваткой памяти. Кроме того, если дата и время объекта класса 
TTime изменились (например, с помощью обращения к ChangeTime() и SetTime()), то 
ненулевой указатель dts должен быть удален и установлен равным нулю, таким 
образом он будет выделен заново в следующем вызове GetSTime(). 
    Вернемся к TIME6.H (листинг 13.18) и рассмотрим строку 19, в которой 
объявляется деструктор для класса TTime. Деструкторы обычно объявляются в 
открытой секции класса, но могут и не объявляться вовсе. Класс может иметь не 
более одного деструктора с именем класса, в котором он объявлен (как и все 
конструкторы). Только объявлению деструктора предшествует тильда (~), которая 
используется в математике для обозначения разности. Деструкторы — антиподы 
конструкторов: то, что создано конструктором, может быть уничтожено деструктором. 
    Реализация деструктора ничем не отличается от других функций-членов. Вы 
можете усовершенствовать код, объявив деструктор встраиваемым. В листинге 
13.19 в строках 21-24 приводится реализация деструктора класса TTime: 

    TTime:: ~TTime() 
    { 
       delete dts;  // Удаление строки,   хранящейся в объекте 
    } 

    Так же, как конструкторы и функции-члены, имя деструктора ~TTime() 
предваряется именем класса и оператором разрешения области видимости TTime::, 
что идентифицирует деструктор как член класса ТТime. Единственный оператор в 
деструкторе TTime удаляет dts. (Можно не проверять dts на содержание 
ненулевого значения; оператор delete проигнорирует нулевые аргументы-указатели.) 
    Если объект класса выходит из области видимости, C++ автоматически 
вызывает деструктор объекта класса, если таковой имеется. Это дает объекту 
удобную возможность для самоочистки, например удаления памяти, выделенной 
конструктором или функцией-членом. 

    Совет
    Помните: конструкторы создают, деструкторы разрушают. Класс может иметь 
много конструкторов, следовательно, программа может создавать объекты 
различными способами. Конечно, класс может иметь не более одного деструктора, 
который вызывается автоматически при уничтожении объекта или при выходе его из 
области видимости. Если необходимо по-разному уничтожать объекты, деструктор 
может вызывать функции-члены, использовать оператор switch и другие средства 
для адекватных действий в различных ситуациях. 


    "Жизнь" объекта 

    Объекты класса могут быть глобальными или локальными в функции. Как и 
переменные обычных типов, объекты класса могут также адресоваться указателями 
и ссылками. Думайте о классах, как о новых типах данных, и используйте их 
соответствующим образом, так же, как и переменные других типов. 
    Природа объекта, а именно: является ли он глобальным, локальным, 
адресуемым указателем и т.д. — влияет на то, когда будут вызываться 
конструкторы и деструктор этого объекта класса. При создании объекта C++ 
автоматически вызывает конструктор этого объекта по умолчанию или другой 
конструктор, который вы укажете в объявлении объекта. Когда объект выходит из 
области видимости, C++ вызывает его деструктор. На рис. 13.5 на временном 
отрезке представлены все эти действия с отметкой типичных событий, 
происходящих в жизни объекта. 
    Следующие замечания помогут прояснить, когда вызываются конструкторы и 
деструкторы для объектов классов различных типов. 

    Глобальные объекты класса 

    Конструкторы глобальных объектов класса вызываются до начала выполнения 
функции main(). Это обеспечивает инициализацию всех глобальных объектов до 
формального начала выполнения программы. При использовании класса TTime 
(листинги 13.18 и 13.19) глобальное объявление 

    TTime today;	


    328            Часть III. Программирование на ANSI C++ 

создает глобальный объект today класса TTime.  До вызова main() C++ 
вызывает для today конструктор TTime по умолчанию, который устанавливает в 
объекте текущую дату и время. 


    Рис. 13.5. События, обычно происходящие с объектом во время его существования


    Для инициализации глобального объекта класса TTime какими-либо 
определенными датой и временем следует использовать следующее объявление: 

   TTime someTime( 9,  4, 1953, 10,  45) ; 

    Наличие списка инициализаторов в круглых скобках вынудит C++ заняться поисками 
в классе TTime конструктора, способного принять указанные аргументы. В данном случае C++ 
вызовет перегруженный конструктор TTime (строки 17-18 листинга 13.18),  который 
инициализирует someTime значением даты и времени 4 сентября 1953 года, 10:45. 
Опять же, это действие выполнится до того, как будет вызвана main(). 
    Деструкторы глобальных объектов класса вызываются как обычно в качестве 
части кода завершения программы, после отработки всех функций завершения, заданных 
вызовами функции atexit() (см. книгу Освоение Borland C++ 4.5. Энциклопедия функции). Конечно, 
деструкторы не вызываются, если программа заканчивается с помощью обращения к abort(). 

    Локальные объекты класса 

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

    void anyFunction(void) 
    { 
       TTime now;     // Инициализируется при каждом вхождении в функцию 
    } 
    
по умолчанию вызывается конструктор TTime для инициализации объекта класса 
now текущими датой и временем каждый раз, когда вызывается функция. 
    Когда функция завершает работу, C++ вызывает деструктор TTime для объекта 
now, что дает объекту возможность "убрать" за собой до того, как он выйдет из 
области видимости. 
    Если вы вызываете exit() для того, чтобы завершить программу, деструкторы 
глобальных объектов классов вызываются как обычно, а деструкторы любых 
существующих автоматических переменных, локальных в функции (включая и те, 
которые локальны в main(), не вызываются.

    Указатели на объекты 

    Указатели могут ссылаться на динамические объекты класса, пространство для 
которых обычно выделяется в куче с помощью new. Вы можете объявить указатель 
pToday на объект класса типа TTime следующим образом: 

    TTime *pToday; 

    Это объявление может быть как глобальным, так и локальным в функции. Как и 
все указатели, pToday необходимо инициализировать до его использования, обычно 
с помощью new: 

    pToday = new TTime; 

    Здесь с помощью оператора new выделяется пространство в куче для объекта 
типа TTime. Адрес первого байта этого объекта присваивается pToday. К тому же, 
C++ вызывает конструктор объекта по умолчанию, который в этом примере 
инициализирует объект текущими датой и временем. 
    Для использования другого конструктора добавьте параметры после имени 
класса. Например, для выделения памяти для объекта TTime и инициализации его с 
помощью вызова конструктора с пятью параметрами можно написать следующее:

    pToday = new TTime(9,   4,   1953,   10,   45); 


    Глава 13. Программирование классов    329 

    Указатели на объекты класса используются в точности так же, как и 
указатели на объекты других типов. Например, следующий оператор выводит дату и 
время объекта класса TTime, на который ссылается pToday: 

    pToday->Display(); 

    Классы похожи на структуры, и использовать указатели на объекты класса вы 
будете чаще всего с помощью оператора -> для ссылки на член адресуемого 
объекта. Конечно, вы можете также использовать оператор * с указателями на 
объекты класса. Например, оператор 

    sp = (*pToday).GetSTime(); 

присваивает переменной sp (которая объявлена, как const char *sp;) 
результат функции-члена GetSTime() для объекта класса, на который ссылается 
pToday. Конечно, оператор 

    sp = pToday->GetSTime(); 

легче для написания и чтения и выполняет аналогичные действия. 
    C++ вызывает деструкторы динамических объектов класса при их удалении. Оператор 

    delete pToday; 

удаляет объект, на который ссылается pToday, освобождая память этого 
объекта в куче для последующего использования. Непосредственно перед 
разрушением объекта C++ вызывает деструктор класса, который может 
воспользоваться удобным случаем для освобождения памяти, используемой в 
объекте. Например, в случае с классом TTime деструктор может освободить 
память, на которую ссылается указатель dts. 

    ЗАМЕЧАНИЕ 
    Не путайте жизнь указателя с жизнью объекта, на который он ссылается. 
Глобальный указатель существует до тех пор, пока программа не завершится, но 
он может ссылаться на множество объектов класса, создаваемых с помощью new и 
удаляемых с помощью delete. Аналогично, автоматические указатели, объявленные 
локальными в функции, создаются при выполнении функции и уничтожаются при ее 
завершении. Но объекты, на которые ссылаются указатели, имеют глобальную 
область дейтвия и должны удаляться явным образом. В функции f(), если вы 
вызовете new для создания объекта в куче, этот объект останется в памяти даже 
после завершения функции. Вы должны явным образом удалять любой подобный 
объект, если вы не хотите, чтобы он оставался в памяти после возврата из функции. 


    Ссылочные объекты 

    Объекты класса могут объявляться как ссылки. Предположим, вы объявили 
глобальный объект класса TTime: 

    TTime today;   // Глобальный объект today 

    Позже, в программе, вы можете объявить ссылку на today следующим образом: 

    TTime &rToday = today;	// Ссылка на today 

    Ссылка rToday — алиас для today, и может использоваться вместо today. 
Например, каждый из следующих двух операторов выведет дату и время: 

    rToday.Display() ; 
    today.Display() ;

    Поскольку ссылки ссылаются на реально существующие объекты, при входе в 
область действия ссылки конструктор класса не вызывается и, соответственно, не 
вызывается деструктор при выходе из области действия ссылки. Эти действия 
выполняются только при создании и удалении самого объекта. 
    Обычно ссылки объявляются в качестве параметров функции   На практике они 
редко используются так, как описано в этом разделе. 

    Объекты-параметры

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

    void anyFunction(TTime t)	
   { 
     t.Display() ; 
   } 


    330     Часть III. Программирование на ANSI C++ 

    Функция anyFunction() имеет единственный параметр t типа TTime. В функции 
вызывается функция-член Display()объекта t для вывода даты и времени, хранящихся 
в параметре. Программа может объявить объект класса TTime:

   TTime  today;                                                       

и затем лередать его по значению функции anyFunction():

   anyyFunction( today ) ;

    Функции также могут объявлять в качестве параметров указатели на класс. 
Передача больших по размерам объектов класса функции по значению приводит к 
выделению пространства нежелательной величины в стеке для таких объектов. В 
таких случаях предпочтительней параметры-указатели:

    Function (TTime *tp) 
    {
        tp->Display();	
    }

    Для вызова этой версии anyFunction() оператор вызова передает адрес объекта 
класса TTime вместо самого объекта:

    anyFunction(&today); 

    Наверное, это самый распространенный способ передачи объектов функции. 
Конечно, параметры функции могут объявляться как ссылки на объекты класса. 
Результаты будут аналогичными, как и в случае использована указателей, только 
внутри функции не будут нужны операции доступа по указателю. Приведем еще одну
версию anyFuuctionO, на этот раз с использованием ссылочного параметра на 
объект класса TTime: 

     void anyFunction(TTime &tr)      // tr - ссылочный параметр	
     {
        tr.Display() ;	// Вызов Display() для объекта, на который ссылается tr                                   
     }
 
    В каком-либо другом месте программы вы можете передать объект класса TTime 
непосредственно в качестве ссылочного параметра anyFunction():	

     anyFunction(today);

    Единственный недостаток ссылочных параметров — то, что такой оператор 
похож на передачу аргумента today по значению. Факт передачи в anyFunction()
ссылки на today из текста совершенно не очевиден.

    Объекты — результаты функций	

    Функции могут возвращать объекты классов непосредственно в качестве 
указателей или как ссылки. Реже всего используется функция, возвращающая 
непосредственно объект класса TTime:

   TTime newTime(void) 
   { 
      TTime t; 
      return t; 
   } 
   
 Локальный объект t (автоматически инициализированный вызовом конструктора 
в начале выполнения функции) возвращается непосредственно. Функция newTime()
может вызываться следующим образом: 

    TTime anotherTime = newTime() ;

    При этом результат функции newTime() копируется в anotherTime. Такой подход 
имеет мало преимуществ, и легче создать новый объект TTime без вызова функции: 

      TTime anotherTime ;  // Аналогично предыдущему                                   

    Чаще функции возвращают указатели и ссылки на объекты класса. Например, 
приведем функцию, выделяющую в памяти новый объект класса TTime и возвращающую 
его адрес: 

    TTime *newTimeP(void) 
    {
      TTime *p = new TTime;      // Выделение и инициализация объекта 
      return p;	// Возвращение адреса объекта в качестве результата функции 
   } 

    Внутри функции указателю р на TTime присваивается адрес объекта класса 
TTime, созданного с помощью new. Указатель существует только внутри функции, 
но объект, на который ссылается этот указатель, — глобальный, следовательно 
функция вправе возвратить его адрес. Оператор может вызвать newTime() для 
получения нового объекта и присвоить его адрес указателю на TTime, например 
tp: 

   Time *tp = newTimeP() ; 


    Глава 13. Программирование классов	331 

    Наконец,  функции могут возвращать ссылки на объекты класса.  Вы можете 
использовать ссылочные функции для ссылки на существующие объекты, такие как 
глобальный объект TTime today: 

   TTime &newTimeR(void) 
   {
      return today; 
   }

    Где-нибудь в другом месте вы можете объявить ссылку на TTime и присвоить 
ей результат newTimeR():

   TTime &tr = newTimeR();

   Теперь ссылка tr — псевдоним для today и может использоваться в операторе вида: 

     newTimeP().Display(); 

    Этот трюк особенно полезен в случае, если функция, подобная newTimeR(), выполняет 
операцию поиска с возможным возвратом ссылки на один из нескольких объектов класса 
TTime, исходя из неких указанных критериев. 

    Массивы объектов класса 

    Так же, как вы можете объявлять массивы обычных типов данных, вы можете 
объявлять и массивы объектов класса. Например, следующее объявление создаст 
массив из 10 элементов объектов TTime: 

    TTime tenTimes[10]; 

    Существует только одно строгое правило, которое необходимо помнить: 
объекты класса, которые будет содержать массив, должны иметь конструкторы по 
умолчанию. Когда создается массив объектов класса, подобный tenTimes, C++ 
вызывает конструктор класса по умолчанию для каждого объекта массива. В случае 
с tenTimes, C++ вызывает 10 раз конструктор по умолчанию TTime, инициализируя 
таким образом объекты tenTimes[0] — tenTimes[9] текущими датой и временем. 
    Листинг 13.22 демонстрирует использование простых массивов объектов 
класса. В программе используется класс TTime, определенный ранее в TIME6.H. 
Скомпилируйте программу из приглашения DOS командой bcc obarray1 time6 и 
запустите получившийся в результате исполняемый файл OBARRAY1.EXE путем ввода 
его имени. Или же из интегрированной оболочки с помощью меню Project откройте 
проект OBARRAY1.IDE и нажмите <Ctrl+F9> для компиляции и запуска. 

    Листинг 13.22.  OBARRAY1.CPP (массивы и конструктор по умолчанию)

    1:	#include <iostream.h> 
    2:	#include <stdiovh> 
    3:	#include "times.h"
    4: 
    5:      main()
    6:	{
    7:	  TTime tarray[6] ;
    8:
    9:	  for (int i=0;   i < 6; i++ )
    10:	    tarray[i].Display();
    11:	  return 0; 
    12:	} 

    В строке 7 объявляется массив tarray из шести объектов TTime. Если вы 
запустите OBARRAY1, вы увидите шесть одинаковых дат и времен, значит C++ 
вызывал конструктор класса для инициализации каждого элемента массива. 
(Запустите программу на выполнение из Turbo Debugger и нажимайте <F7> для 
трассировки операторов и проверки того факта, что конструктор TTime будет 
вызван шесть раз.) 
    Если вы хотите, чтобы C++ вызывал различные конструкторы, вы должны явным 
образом инициализировать каждый элемент массива. Вы можете сделать это 
непосредственно в объявлении массива, как это показано в листинге 13.23. 
Скомпилируйте программу, как вы это уже делали с OBARRAY1. 

    Листинг 13.23.  OBARRAY2.CPP (объявление массивов и альтернативных конструкторов) 

    1:	#include <iostream.h> 
    2.	#include <stdio.h> 
    3:	#include "timee.n" 
    4: 
    5:	main()                                                                
    6:	{                                         .                       
    7:	 TTime tarray[6] = {   
    8:        TTime(), 
    9:        TTime(8), 


    332     Часть III. Программирование на ANSI C++ 


    10:	   TTime(8,   1), 
    11:	   TTime(8,   1,  1996), 
    12:	   TTime(8,   1,  1996,  8), 
    13:	   TTime(8,   1,   1996,   8,   30) 
    14:	 }; 
    15:	 for (int i = 0;   i < 6;   i++) 
    16:	   tarray[i].Display() ;
    17:	 return 0; 
    18:    }

    В строках 7-14, как и в OBARRAY1.CPP, объявляется массив из шести объектов 
TTime. Конечно, в данном случае объекты массива инициализируются 
конструкторами, указанными в скобках, аналогично тому, как мы инициализировали 
массив структур. В строке 8 инициализируется объект tarray[0] с помощью вызова 
конструктора по умолчанию TTime. В строках 9-13 инициализируются остальные 
объекты массива путем вызова перегруженных конструкторов. 
    Нет простого способа инициализировать одни массивы объектов класса с 
помощью конструктора по умолчанию, а другие с помощью альтернативного 
конструктора. Если у вас возникла такая необходимость, вероятно, лучше 
объявить массив, как в OBARRAY1, а затем инициализировать заново значения 
нужных объектов требуемым образом. Прием, продемонстрированный в OBARRAY2, 
слишком громоздкий для широкого применения на практике за исключением 
небольших по размеру массивов. 
    Массивы объектов классов могут также размещаться в куче и адресоваться с 
помощью указателей. В этом случае для работы с массивом необходимо предпринять 
некоторые дополнительные предосторожности. В листинге 13.24 поясняется эта 
мысль. Скомпилируйте программу, как вы это уже делали с OBARRAY 1. 

    Иистинг 13.24.   OBARRAY3.CPP (поддержка динамических массивов объектов класса)

    1:	#include	<iostream.h> 
    2:	#include	<stdio.h> 
    3:	#include	"time5.h" 
    4:
    5:	main() 
    6:	{ 
    7:	  TTime *tarrayP; 
    9:	  tarrayP = new TTime[6]; // Создание динамического массива 
    10:	  for (int 1=0; i < 6; i++) 
    11:	    tarrayP[i].Display(); 
    12:	  delete[] tarrayP; // Обратите внимание на особую форму delete[] 
    13:	  return 0;     
    14:	} 

    В строке 7 объявляется tarrayP в качестве указателя на объект класса 
TTime. Для инициализации указателя в строке 9 вызывается new, выделяющий в 
куче массив из шести объектов TTime с присваиванием tarrayP адреса первого 
объекта массива. В связи с обращением к new C++ шесть раз автоматически 
вызывает конструктор по умолчанию класса TTime для инициализации каждого 
объекта в массиве. 
    Как и в случае с другими динамическими объектами, вы должны удалить 
динамический массив объектов класса по завершении его использования. Конечно, 
удаление подобных массивов вызывает проблемы. Из-за неспособности С и C++ 
различать указатели и массивы программы должны использовать особую форму 
оператора delete[] для обеспечения вызова всех деструкторов при удалении 
динамического массива объектов класса надлежащим образом. Если бы строка 12 в 
OBARRAY3 была написана как 

    delete tarrayP;	// ??? 

то компилятор не смог бы узнать, на что ссылается tarrayP — на один объект 
класса TTime или на массив объектов. Для указания компилятору, что tarrayP 
ссылается на массив, следует добавить пустые скобки к delete: 

    delete[] tarrayP;         // Удаление массива объектов класса TTime. 

    Такая специальная команда обеспечит вызов деструктора для каждого объекта 
в массиве. 

    ЗАМЕЧАНИЕ 
    В прошлых версиях C++ необходимо было указывать число элементов в 
удаляемом массиве объектов класса и вам пришлось бы использовать операторы 
вида delete[6] tarrayP для удаления массива из шести объектов класса TTime, на 
который ссылается tarrayP. Применение этого приема ныне не допускается, и 
Borland C++ игнорирует любые значения внутри скобок delete[]. Если вы укажете 
размер массива в скобках, компилятор выдаст вам предупреждение "Array size for 
'delete' ignored in function x<)" (Размер массива для delete проигнорирован в 
функции х()).


    Глава 13. Программирование классов 333 


    Резюме 

  *  Объектно-ориентированное программирование, или ООП, помогает программистам 
уменьшить сложность программ и повторно использовать существующий код. 
  *  В C++ принципы ООП воплощены в классе — инструменте для создания новых 
типов данных, которые ведут себя подобно встроенным, таким, как int или 
double. Используйте объекты класса так же, как и объекты других типов. 
  *  Классы похожи на структуры, но в качестве членов могут иметь и функции.  
Классы инкапсулируют данные и функции, контролируя таким образом доступ к 
программным данным. 
  *  Следует использовать спецификатор доступа public для того, чтобы сделать 
члены класса открытыми для всех пользователей этого класса. Для того чтобы 
закрыть доступ к членам класса, следует использовать спецификатор доступа 
private. Только функции-члены класса могут иметь прямой доступ к закрытым 
данным-членам класса и вызывать закрытые функции-члены. В главе 14 
вводится понятие третьего спецификатора доступа — protected. 
  *  Члены класса закрыты по умолчанию. Конечно, для ясности лучше использовать 
спецификатор доступа private явным образом для идентификации закрытых членов. 
  *  Функции-члены класса реализуются точно так же, как и обычные функции, но 
их заголовки включают в себя имя класса и оператор разрешения области 
видимости. Для класса Т функция-член, объявленная как void f(void);,
реализуется как void T::f(void);, указывая компилятору, что функция f() — член 
класса Т. 
  *  Каждая функция-член класса получает скрытый параметр с именем this, 
содержащий адрес объекта, для которого была вызвана эта функция. Для объекта х 
класса Т в функции-члене T::f() this ссылается на х. Обычно   нет   необходимости   
использовать this в операторах, поскольку функции-члены имеют непосредственный 
доступ к членам объекта класса. 
  *  Функции  могут иметь объекты класса в  качестве  параметров,   которые 
могут быть параметрами  по значению, указателями или ссылками. Функции могут 
также возвращать объекты класса, хотя обычно они возвращают указатели или 
ссылки на них.	
  *  Тела встраиваемых функций-членов вставляются непосредственно в 
скомпилированный код.  Конечно, ключевое слово inline — скорее лишь указание 
компилятору, которое может быть принято к сведению, а может, и нет. Написание 
коротких встраиваемых функций — лучший способ предотвратить интерпретацию 
компилятором объявлений inline как обычных объявлений функций-членов. 
  *  Как и обычные функции, функции-члены могут быть перегружены. В классе 
может быть неограниченное число функций, имеющих одно и то же имя, при 
условии, что каждая функция отличается от других хотя бы одним параметром. 
Перегруженные   функции-члены   помогают   сократить   число   имен,   
которые программистам приходится изобретать для выполнения похожих операций. 
Разумеется, перегруженные функции-члены отличаются одна от другой и должны быть 
реализованы индивидуально. 
  *  Функции-члены могут объявлять значения параметров по умолчанию для 
аргументов, которые можно не задавать. 
  *  В классе может быть объявлен один или несколько конструкторов для 
обеспечения автоматической инициализации объекта класса. Конструктор имеет 
такое же имя, как и класс, в котором он определен, и не имеет типа данных для 
возврата. Конструктор класса по умолчанию не имеет параметров. Прочие 
конструкторы могут иметь один или более параметров, обычно для обеспечения 
инициализации объектов класса  начальными  значениями.   Параметры 
конструктора могут быть  любого типа,   за  исключением класса, которому он 
принадлежит. C++ вызывает один из конструкторов при создании объекта класса. 
  *  В классе может быть объявлен деструктор (но только один). Деструктор 
обычно удаляет динамические переменные, на которые ссылаются данные-члены 
объекта класса, но может выполнять и другие функции очистки. C++ вызывает 
деструктор, когда объект класса выходит из области видимости или когда он 
удаляется. 
  *  Программы могут создавать массивы объектов классов. Класс должен иметь 
конструктор по умолчанию, который C++ вызывает по одному разу на каждый объект 
массива. Массивы могут также объявляться и инициализироваться с помощью явных 
обращений к перегруженным конструкторам. 
  *  При выходе массива объектов класса из области видимости C++ автоматически 
вызывает деструктор класса для каждого объекта массива. 
  *  Динамические массивы объектов класса могут создаваться с помощью  
new, размещаться в куче и адресоваться указателями. Конечно,  поскольку 
С и  C++  не делают различий между указателями и массивами, следует 
использовать delete[] (с пустыми скобками) а не delete (без скобок), для 
удаления динамических массивов объектов класса. Такая форма delete обеспечит 
вызов деструктора класса для каждого элемента в массиве. 

    334 Часть III. Программирование на ANSI C++ 


    Упражнения 


    ЗАМЕЧАНИЕ 
    Для всех упражнений,  ссылающихся на класс TTime,  используйте объявление из 
заголовочного файла TIME6.H (листинг 13.18) и реализацию класса в TIME6.CPP 
(листинг 13.19). 

    13.1. Разработайте класс кнопки, который вы можете использовать в 
моделировании, где необходимы переключатели вкл/выкл. В вашем классе должны 
быть соответствующие функции-члены для управления объектом класса кнопки.
    13.2. С помощью класса TTime (листинги 13.18 и 13.19) напишите программу 
DT.CPP, выводящую текущие дату и время. 
    13.3. На основании вашего ответа на упражнение 13.2, напишите оператор, 
одновременно заменяющий дату в вашем объекте на завтрашнюю. 
    13.4. Используя класс TTime, напишите программу с именем DAY.CPP, 
сообщающую день недели для любой даты, введенной с помощью командной строки 
DOS. Например, команда day jan 5 1997 должна сообщить день недели для 
указанной даты. 
    13.5. Добавьте функцию-член в класс TTime для взятия и изменения даты и 
времени, используя длинное целое в качестве аргумента, представляющее число 
секунд с 1 января 1970 года. Также добавьте конструктор для инициализации 
объекта класса TTime с помощью длинного целого значения секунд. В вашем ответе 
вы можете использовать встраиваемые функции-члены. 
    13.6. Создайте класс, способный сохранять строку в куче. Должна 
существовать возможность передачи строки объекту вашего класса и затем 
получения указателя на такую же строку. Должна быть также возможность 
изменения строки объекта класса. Используйте конструкторы и деструктор для 
обеспечения всех необходимых автоматических инициализаций и очистки объекта. 
    13.7. Напишите программу, использующую ваш строковый объект из упражнения 
13.6 для чтения и отображения текстового файла. 

    Глава 13. Программирование классов 335 
