Закрытые данные-элементы можно изменять только с помощью функ- ций-элементов (и дружественных функций) класса. Типичным изменением могла бы быть корректировка баланса клиентов банка (например, закрытого элемента данных класса BankAccount) с помощью функции-элемента computelnterest.
Классы часто предусматривают открытые функции-элементы, позволяющие клиентам класса устанавливать (т.е. записывать) или получать (т.е. читать) значения закрытых данных-элементов. Эти функции можно не называть конкретно «set» или «get», но все же часто применяют именно эти названия. Более точно, функция-элемент, которая устанавливает элемент данных in- terestRate, должна была бы называться setlnterestRate, а функция-элемент, которая читает элемент данных interestRate, должна была бы называться getlnterestRate. Читающие функции обычно называют также функциями «запросов».
Казалось бы, что предоставление возможностей как установки, так и чтения, по существу, то же самое, что задание открытых данных-элементов. Однако в С++ существует одна тонкость, которая делает этот язык таким привлекательным для создания программного обеспечения. Если данные-эле- менты открытые, то они могут быть прочитаны или записаны по желанию с помощью любой функции в программе. Если данные-элементы закрытые, то открытая функция «get», позволяет другим функциям читать при желании эти данные, причем функция «get» управляет форматированием и отображением данных. Открытая функция «set» может, а более точно — должна тщательно анализировать любую попытку изменить значение закрытого элемента данных. Это должно обеспечивать гарантию, что новое значение соответствует этому элементу данных. Например, должны быть отвергнуты попытки установить день месяца, равный 37, отрицательный вес человека, численную величину символьного значения, значение оценки, равное 185, при шкале оценок от 0 до 100 и т.д.
Замечание по технике программирования 6.17
Задание закрытых данных-элементов и управление доступом к ним, особенно доступом к записи данных-элементов, посредством открытых функций-элементов помогает гарантировать целостность данных.
Выгоды целостности данных не вытекают просто автоматически из того, что данные-элементы объявляются закрытыми. Программист должен обеспечивать проверку их правильности. С++ предоставляет среду программирования, в которой программист может проектировать хорошие программы в удобной ему манере.
Хороший стиль программирования 6.10
функции-элементы, которые записывают значения закрытых данных, должны проверять правильность предполагаемых новых значений; если они неправильные, то эти функции должны установить закрытые данные-элементы в соответствующее им непротиворечивое состояние.
Клиенты класса должны быть уведомлены о попытке присвоить данным- элементам неверные значения. По этой причине функции записи данных- элементов класса часто оформляются так, чтобы они возвращали значения, указывающие на то, что была совершена попытка присвоить объекту класса неверное значение. Это дает возможность клиентам класса проверить значения, возвращаемые функцией записи, чтобы определить, является ли объект, которым они манипулируют, допустимым, и предпринять соответствующие действия, если объект недопустимый. В упражнениях вас попросят изменить программу на рис. 6.10 так, чтобы возвращать соответствующие значения ошибок из записывающих функций.
Программа на рис. 6.10 расширяет наш класс Time так, чтобы он включал функции чтения и записи закрытых данных-элементов hour, minute и second. Функции записи жестко управляют установкой данных-элементов. Попытки задать любым данным-элементам неправильные значения вызывают присваивание этим данным-элементам нулевых значений (и, таким образом, приведение данных-элементов в непротиворечивое состояние). Каждая функция чтения просто возвращает соответствующее значение данных-элементов. Программа сначала использует функции записи, чтобы задать правильные значения закрытым данным-элементам объекта t класса Time, затем использует функцию чтения, чтобы вывести эти значения на экран. Далее функции записи пытаются задать элементам hour и second неправильные значения, а элементу minute — правильное, и затем функции чтения направляют эти значения на экран. Результат подтверждает, что неправильные значения вызывают установку данных-элементов в нулевое состояние. В итоге программа устанавливает время 11:58:00 и прибавляет 3 минуты при вызове функции incrementMinutes. Функция incrementMinutes не является элементом класса; поэтому она использует функции-элементы записи и чтения для соответствующего увеличения элемента minute. Это функционирует правильно, но снижает производительность из-за многократных вызовов функций. В следующей главе мы обсудим запись дружественных функций как средства устранения этого недостатка.
Типичная ошибка программирования 6.8
Конструктор может вызывать другие функции-элементы класса такие, как функции записи и чтения. Но поскольку конструктор инициализирует объект, данные-элементы могут в этот момент еще не быть в непротиворечивом состоянии. Использование данных-элементов до того, как они получили соответствующие начальные значения, может вызвать ошибки.
Функции записи особенно важны с точки зрения разработки программного обеспечения, поскольку они могут производить проверку правильности данных. Функции записи и чтения имеют и другие достоинства при разработке программного обеспечения.
// TIME3. Н Ч // Объявление класса Time.
// Функции-элементы, определены в TIME3.CPP
// Предотвращение многократного включения заголовочного файла iifndef Т1МЕЗ_Н ttdefine TIME3 Н
class Time { public:
// конструктор |
0) ; |
Time(int = 0, int = 0, int
// функции записи
void setTime(int, int, int);
void setHour(int); void setMinute(int); void setSecond(int);
//установка часов, минут, // секунд // установка часа // установка минут // установка секунд
// функции чтения int getHour(); int getMinuteO; int getSecond(); |
// возвращает час // возвращает минуты // возвращает секунды
void printMilitary(); void printStandard();
private:
int hour; int minute; int second;
// выводит военное время // выводит стандартное время
// | 0 | - 23 |
// | 0 | - 59 |
// | 0 | - 59 |
tendif
// Определения функций-элементов класса Time. #include "time3.h" #include
// Функция конструктор для задания начальных значений // закрытых данных вызывает функцию-элемент setTime, чтобы // установить значения переменных по умолчанию равными нулю // (смотри определение класса).
Time::Time(int hr, int min, int sec) { setTime(hr, min, sec)
// Установка значений часов, минут, секунд.
void Time::setTime(int h, int m, int s)
{
hour = (h >= 0 && h < 24) ? h : 0; minute = (m >= 0 && m < 60) ? m : 0; second = (s >= 0 && s < 60) ?s : 0;
}
.'••ч*? // Установка значения часа
;Г"3. void Time: : setHour (int h) { hour = (h >= 0 && h < 24) ? h : 0;}
g. Щ // Установка значения минут •Д* > void Time:: setMinute (int m)
{ minute = (m >= 0 && m < 60) ? m : 0; }
// Установка значения секунд void Time :: setSecond (int s)
{ second = (s >= 0 && s < 60) ? s : 0; }
.-. ■ // Получение значения часа
int Time::getHour() { return hour; }
// Получение значения минут
int Time::getMinute() { return minute; }
// Получение значения секунд ■ лУ*^ int Time: : getSecond () { return second; }
^ • // Отображение времени в военном формате: HH:MM:SS
** void Time :: printMilitary ()
f
cout « (hour < 10 ? "0" : " " ) « hour « ":"
« (minute < 10 ? "0" : " " ) « minute « ":" « (second < 10 ? "0" : " " ) « second;
}
// Отображение времени в стандартном формате: HH:MM:SS AM (или РМ) void Time:: printStandard ()
(
АУ» cout « ((hour ===== 0 | | hour ==12) ? 12 : hour % 12) « ": "
tjA « (minute < 10 ? "0" : " " ) « minute « ":"
« (second < 10 ? "0" : " " ) « second
« (hour < 12 ? "AM" : "PM");
// FIG6_10.CPP
// Демонстрация функций записи и чтения класса Time
#include #include "time3.h"
void incrementMinutes(Time &, int);
main() {
Time t;
t.setHour(17); t.setMinute(34) ; t.setSecond(25);
cout << "Результат установки всех правильных значений:" << endl « " Час: " « t.getHour() « " Минуты: " « t.getMinute ()
« " Секунды: " « t.getSecond() « endl « endl;
t.setHour(234); t.setMinute(43) ; t.setSecond (6373);
cout << "Результат попытки установить неправильные часы и " << "секунды: " « endl « " Час: " « t.getHour () « " Минуты: " << t.getMinute()
« " Секунды: " <
t.setTime(11, 58, 0); incrementMinutes(t, 3) ; return 0;
void incrementMinutes(Time &tt, int count) {
cout << "Увеличение минут на " << count
<< endl << "Начальное время: "; tt.printStandard();
for (int i = 1; i <= count; i++) {
tt.setMinute((tt.getMinute() + 1) % 60);
if (tt.getMinute() == 0)
tt.setHour((tt.getHour() + 1) % 24);
cout << endl << "минуты +1: "; tt.printStandard() ;
}
cout << endl;
h
Рис. 6.10. Использование функций записи и чтения (часть 3 из 4)
Результат установки всех правильных значений: Час: 17 Минуты: 34 Секунды: 25
Результат попытки установить неправильные часы и секунды: Час: 0 Минуты: 43 Секунды: О
Увеличение минут на 3 Начальное время: 11: 58:ООАМ минуты +1: 11: 59:ООАМ минуты + 1: 12:00:00РМ минуты + 1: 12:01:00РМ
Рис. 6.10. Использование функций записи и чтения (часть 4 из 4) Замечание по технике программирования 6.18
Доступ к закрытым данным посредством функций записи и чтения не только защи щает данные-элементы от присваивания им неправильных значений, но и отделяет клиентов класса от внутреннего представления данных-элементов. Таким образом, если внутреннее представление этих данных по каким-либо причинам (обычно из-за требований сокращения объема памяти или повышения производительности) изме няется, достаточно изменить только функции-элементы, а клиентам не требуется вносить никаких изменений, пока остается неизменным интерфейс функций-элемен тов. Однако, возможно, потребуется перекомпиляция клиентов данного класса.
Комментариев нет:
Отправить комментарий