Мы еще раз особо отмечаем принцип наименьших привилегий как один из наиболее фундаментальных принципов создания хорошего программного обеспечения. Рассмотрим один из способов применения этого принципа к объектам.
Некоторые объекты должны допускать изменения, другие — нет. Программист может использовать ключевое слово const для указания на то, что объект неизменяем — является константным и что любая попытка изменить объект является ошибкой. Например,
const Time noon(12, 0, 0);
объявляет как константный объект noon класса Time и присваивает ему начальное значение 12 часов пополудни.
Замечание по технике программирования 7.1
Объявление константного объекта помогает провести в жизнь принцип наименьших
привилегий. Случайные попытки изменить объект отлавливаются во время компиляции и не вызывают ошибок во время выполнения.
Компиляторы С++ воспринимают объявления const настолько неукоснительно, что в итоге не допускают никаких вызовов функций-элементов константных объектов (некоторые компиляторы дают в этих случаях только предупреждения). Это жестоко, поскольку клиенты объектов возможно захотят использовать различные функции-элементы чтения «get», а они, конечно, не изменяют объект. Чтобы обойти это, программист может объявить константные функции-элементы; только они могут оперировать константными объектами. Конечно, константные функции-элементы не могут изменять объект — это не позволит компилятор.
Константная функция указывается как const и в объявлении, и в описании с помощью ключевого слова const после списка параметров функции, но перед левой фигурной скобкой, которая начинает тело функции. Например, в приведенном ниже предложении объявлена как константная функция-элемент некоторого класса А
int А::getValue () const {return privateDateMember};
которая просто возвращает значение одного из данных-элементов объекта. Если константная функция-элемент описывается вне определения класса, то как объявление функции-элемента, так и ее описание должны включать const.
Здесь возникает интересная проблема для конструкторов и деструкторов, которые обычно должны изменять объект. Для конструкторов и деструкторов константных объектов объявление const не требуется. Конструктор должен иметь возможность изменять объект с целью присваивания ему соответствующих начальных значений. Деструктор должен иметь возможность выполнять подготовку завершения работ перед уничтожением объекта.
Программа на рис. 7.1 создает константный объект класса Time и пытается изменить объект неконстантными функциями-элементами setHour, setMinute и setSecond. Как результат показаны сгенерированные компилятором Borland С++ предупреждения. Опция компилятора была установлена такой, чтобы в случае появления любого предупреждения компилятор не создавал исполняемого файла.
// TIME5.H
// Объявление класса Time. // Функции-элементы описаны в TIMES.CPP
iifndef TIME5_H #define TIME5 H
class Time { public: Time(int = 0, int |
0, int = 0); // конструктор по умолчанию
// функции записи set void setTime(int, int, int) void setHour(int); void setMinute(int); void setSecond(int);
1 |
// установка времени
// установка часа
// установка минут
// установка секунд
.••ям |
// функции чтения get (обычно объявляются const) int getHour() const; // возвращает значение часа
int getMinute() const; // возвращает значение минут
int getSecond() const; // возвращает значение секунд
// функции печати (обычно объявляются const) void printMilitary() const; // печать военного
// времени
void printStandard() const; // печать стандартного
// времени
private: int hour; int minute; int second; } ; |
//0-23 // 0-59 //0-59 |
ИМ |
iendif
Рис. 7.1. Использование класса Time с константными объектами и константными функциями-элементами (часть 1 из 3)
Хороший стиль программирования 7.1
Объявляйте как const все функции-элементы, которые предполагается использовать с константными объектами.
Типичная ошибка программирования 7.1
Описание константной функции-элемента, которая изменяет данные-элементы объ екта.
Типичная ошибка программирования 7.2
Описание константной функции-элемента, которая вызывает неконстантную функ цию-элемент.
// TIME5.CPP
// Описания функций-элементов класса Time, finclude <iostream.h> iinclude "time5.h"
// Функция конструктор для инициализации закрытых данных. // По умолчанию значения равны 0 (смотри описание класса). 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;
// Установка значения часа
void Time::setHour(int h) { hour = (h >= 0 && h < 24) ? h : 0; }
// Установка значения минут 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() const { return hour; ) // Чтение значения минут
int Time::getMinute() const { return minute; } // Чтение значения секунд
int Time::getSecond() const ( return second; }
// Отображение времени в военном формате: HH:MM:SS void Time::printMilitary() const
{
cout « (hour < 10 ? "0": "") « hour « ":" « (minute < 10 ? "0":"" ) « minute« ":' « (second < 10 ? "0":"") « second;
}
// Отображение времени в стандартном формате: HH:MM:SS AM // (или РМ)
void Time::printStandard() const {
cout « ((hour == 12) ? 12
« (minute < 10 ? "0"
« (second < 10 ? "0"
« (hour< 12 ? "AM"
hour % 12)« ":" "") « minute « ' "") « second : "PM") ;
Рис. 7.1. Использование класса Time с константными объектами и константными функциями-элементами (часть 2 из 3)
// FIG7_1.CPP
// Попытка получить доступ к константному объекту //с не-константными функциями-элементами.
#include ciostream.h> #include "time5.h"
main ( )
// ОШИБКА // ОШИБКА // ОШИБКА |
const Time t(19, 33, 52);
t.setHour(12) ; t.setMinute(20) ; t.setSecond(39);
return 0;
// константный объект
не-константная функция элемент не-константная функция элемент не-константная функция элемент
}
Compiling FIG7_1.CPP:
Warning FIG7_1.CPP: Non-const function
Time::setHour(int) called for const object Warning FXG7 l.CPP: Non-const function
Time::setMinute(int) callers for const object Warning FIG7 1.CPP: Non-const function
Time::setSecond(int) called for const object
Рис. 7.1. Использование класса Time с константными объектами и константными функциями-элементами (часть 3 из 3)
Типичная ошибка программирования 7.3
Вызов неконстантной функции-элемента для константного объекта.
Типичная ошибка программирования 7.4
Попытка изменить константный объект.
Замечание по технике программирования 7.2
Константная функция-элемент может быть перегружена неконстантным вариантом. Выбор того, какая из перегруженных функций-элементов будет использоваться, осу ществляется компилятором автоматически в зависимости от того, был объявлен объект как const или нет.
Константный объект не может быть изменен с помощью присваивания, так что он должен получить начальное значение. Если данные-элементы класса объявлены как const, то надо использовать инициализатор элементов, чтобы обеспечить конструктор объекта этого класса начальными значением данных-элементов. Рис. 7.2 демонстрирует использование инициализатора элементов для задания начального значения константному элементу increment класса Increment. Конструктор для Increment изменяется следующим образом:
Increment :: Increment (int с, int i) : increment (i) { count = c; }
Запись : increment (i) вызывает задание начального значения элемента increment, равного i. Если необходимо задать начальные значения сразу нескольким элементам, просто включите их в список после двоеточия, разделяя запятыми. Используя инициаторы элементов, можно присвоить начальные значения всем данным-элементам.
// FIG7_2.CPP t1^ // Использование инициализаторов элементов для * //инициализации данных константного встроенного типа. #include
class Increment { public:
Increment(int с = 0, int i = 1);
void addlncrement() { count += increment; }
|
void print () const;
private:
int count;
const int increment; // константный элемент данных
} ;
// Конструктор класса Increment Increment::Increment(int с, int i)
: increment(i) // инициализатор константного элемента
{ count = с; }
// Печать данных
void Increment::print() const
{
cout << "count = " << count
<< ", increment = " << increment << endl;
}
t main() {
Increment value(10, 5);
cout << "Перед приращением: "; value.print();
for (int j = 1; j <= 3; j++) { value.addlncrement();
cout << "После приращения " << j «": "; value.print();
}
return 0;
}
Перед приращением: count = 10, increment = 5 После приращения 1: count = 15, increment = 5 После приращения 2: count = 20, increment = 5 После приращения 3: count = 25, increment = 5
Рис. 7.2. Использование инициализаторов элементов для инициализации данных константного
встроенного типа
Рисунок 7.3 иллюстрирует ошибки компиляции, выдаваемые компилятором Borland С++ в программе, которая пытается задать начальное значение элементу increment, используя оператор присваивания, а не инициализатор элементов.
// FIG7_3.CPP
// Попытка инициализировать данные константного // встроенного типа с помощью присваивания. #include ciostream.h>
class Increment { public:
Increment(int с = 0, int i = 1); void addlncrement() { count += increment; } void print() const; private:
int count;
const int increment;
};
// Конструктор класса Increment Increment::Increment(int c, int i) { // Константный элемент 'increment'
//не получает начального значения count = с;
increment = i; // ОШИБКА: константный объект не может
// быть изменен
}
// Печать данных
void Increment::print() const {
cout « "count = " << count
<< ", increment = " << increment « endl;
main() {
Increment value(10, 5);
cout « "Перед приращением: "; value.print();
for (int j = l; j <= 3; j++) { value.addlncrement(); cout « "После приращения " << j <<": value.print();
}
return 0;
}
Compiling FIG7_3.CPP:
Warning FIG7_3.CPP 18: Constant member 'increment' is
not initializatied Error FIG7_3.CPP 20: Cannot modify a const object Warning FIG7 3.CPP 21: Parameter 'i' is never used
Рис. 7.3. Ошибочная попытка задать начальное значение данным константного встроенного
типа с помощью присваивания
Типичная ошибка программирования 7.5
Нет инициализаторов константных данных-элементов.
Замечание по технике программирования 7.3
Константные элементы класса (объекты и «переменные») должны полумать начальные значения с помощью инициализаторов элементов. Присваивания недопустимы.
Комментариев нет:
Отправить комментарий