ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ В C++ (4-Е ИЗДАНИЕ) (часть 6) онлайн
Пример со счетчиком
В качестве примера мы создадим класс, объекты которого могут быть полезны практически для любой программы. Счетчик — это средство, предназначенное для хранения количественной меры какой-либо изменяющейся величины. Счет- чик может хранить число обращений к файлу, число раз, которое пользователь нажал клавишу Enter, или количество клиентов банка. Как правило, при наступ- лении соответствующего события счетчик увеличивается на единицу (инкремен- тируется). Обращение к счетчику происходит, как правило, для того, чтобы уз- нать текущее значение той величины, для измерения которой он предназначен.
Допустим, что счетчик, который мы сейчас создадим, будет важной частью нашей программы, и многие из ее функций будут использовать значение этого счетчика. В процедурных языках, таких, как C, счетчик, скорее всего, был бы представлен в виде глобальной переменной. Но, как мы уже говорили в главе 1, использование глобальных переменных усложняет разработку программы и не- безопасно с точки зрения несанкционированного доступа со стороны функций. Наш следующий пример, COUNTER, использует такой счетчик, значение которого может быть изменено только с помощью его собственных методов.
// counter.cpp
// счетчик в качестве объекта
#include <iostream>
using namespace std;
//////////////////////////////////////////////////////////
class Counter
{
private:
unsigned int count; // значение счетчика
public:
Counter() : count(0) // конструктор
{ /* пустое тело */ }
void inc_count() // инкрементирование счетчика
{ count++; }
int get_count() // получение значения счетчика
{ return count; }
};
//////////////////////////////////////////////////////////
int main()
{
Counter c1, c2; // определение с инициализацией
cout <<"\nc1=" << c1.get_count(); // вывод
cout <<"\nc2=" << c2.get_count();
c1.inc_count(); // инкрементирование c1
c2.inc_count(); // инкрементирование c2
c2.inc_count(); // инкрементирование c2
cout <<"\nc1=" << c1.get_count(); // вывод
cout <<"\nc2=" << c2.get_count();
cout << endl;
return 0;
}
Класс Counter имеет единственное поле count типа unsigned int, поскольку значение счетчика не может быть отрицательным, и три метода: конструктор Counter(), который мы рассмотрим чуть позже, inc_count(), инкрементирующий поле count, и get_count(), возвращающий текущее значение счетчика.
Автоматическая инициализация
Когда создается объект типа Counter, нам хотелось бы, чтобы его поле count было инициализировано нулевым значением, поскольку большинство счетчиков на- чинают отсчет именно с нуля. Мы могли бы провести инициализацию с помо- щью вызова функции set_count() с аргументом, равным нулю, или создать специ- альный метод zero_count(), обнуляющий значение функции. Недостаток такого подхода заключается в том, что эти функции необходимо вызывать явно каждый раз при создании объекта типа Counter:
Counter c1; // при определении объекта
c1.zero_count(); // это необходимое действие
Подобные действия легко могут привести к неправильной работе всей про- граммы, поскольку программисту для этого достаточно забыть проинициализи- ровать хотя бы одну переменную после ее создания. Если в программе создается множество таких переменных, гораздо проще и надежнее было бы инициализи- ровать их автоматически при создании. В нашем примере конструктор Counter() выполняет эти действия. Конструктор вызывается автоматически при создании каждого из объектов. Таким образом, в функции main() оператор
Counter c1, c2;
создает два объекта типа Counter. При создании каждого из них вызывается конст- руктор Counter(), присваивающий полю counter нулевое значение. Таким образом, кроме создания переменных, данный оператор еще присваивает их полям нулевое значение.
Имя конструктора
У конструкторов есть несколько особенностей, отличающих их от других мето- дов класса. Во-первых, имя конструктора в точности совпадает с именем класса (в нашем примере таким именем является Counter). Таким образом, компилятор отличает конструкторы от других методов класса. Во-вторых, у конструкторов не существует возвращаемого значения. Это объясняется тем, что конструктор автоматически вызывается системой, и, следовательно, не существует вызывающей программы или функции, которой конструктор мог бы возвратить значение. Следо- вательно, указание возвращаемого значения для конструктора не имеет смысла. Отсутствие типа возвращаемого значения у конструкторов является вторым при- знаком, по которому компилятор может отличить их от других методов класса.
Список инициализации
Одной из наиболее часто возлагаемых на конструктор задач является инициа- лизация полей объекта класса. Для каждого объекта класса Counter конструктор выполняет инициализацию поля count нулем. Вы, вероятно, ожидали, что это
действие будет произведено в теле конструктора приблизительно следующим образом:
Counter()
{ count = 0; }
Такая форма записи не рекомендуется, несмотря на то, что она не содержит ошибок. Инициализация в нашем примере происходит следующим образом:
Counter() : count(0) { }
Инициализация расположена между прототипом метода и телом функции и предварена двоеточием. Инициализирующее значение помещено в скобках пос- ле имени поля.
Если необходимо инициализировать сразу несколько полей класса, то значе- ния разделяются запятыми, и в результате образуется список инициализации:
SomeClass() : m1(7), m2(33), m3(4) { }
Причины, по которым инициализация не проводится в теле конструктора, достаточно сложны. Инициализация полей с помощью списка инициализа- ции происходит до начала исполнения тела конструктора, что в некоторых ситуациях бывает важно. Так, например, список инициализации — это единст- венный способ задать начальные значения констант и ссылок. В теле конструк- тора, как правило, производятся более сложные действия, чем обычная инициа- лизация.
Результаты работы программы со счетчиком
В функции main() рассматриваемой нами программы создаются два объекта класса Counter с именами c1 и c2. Затем на экран выводятся значения полей каж- дого из объектов, которые, согласно нашей задумке, должны быть инициализи- рованы нулевыми значениями. Далее значение счетчика c1 инкрементируется один раз, а значение счетчика c2 — два раза, и программа вновь заставляет объек- ты вывести значения своих полей на экран (что является в данном случае вполне корректным). Результат работы программы выглядит следующим образом:
c1=0 c2=0 c1=1 c2=2
Для того чтобы убедиться в том, что конструктор функционирует именно так, как мы описали выше, заставим его печатать сообщение во время выполнения:
Counter() : count(O)
{ cout << "Конструктор\n"; }
Теперь результат работы программы будет выглядеть следующим образом:
Конструктор Конструктор
c1=0
c2=0 c1=1 c2=2
Как можно видеть, конструктор исполняется дважды: первый раз — для пе- ременной c1, второй раз — для переменной c2, во время выполнения оператора
Counter c1, c2; в функции main().
Конструкторы и собственные типы данных
Разработчики компиляторов для языков C, VB или C++ должны позаботиться о том, чтобы для любой переменной стандартного типа, которую программист оп- ределяет в своей программе, вызывался необходимый конструктор. Например, если в программе встречается определение переменной типа int, где-то должен существовать конструктор, который выделит для этой переменной четыре байта памяти. Таким образом, научившись создавать свои собственные конструкторы, мы можем выполнять задачи, с которыми сталкиваются разработчики компиля- торов. Мы сделали еще один шаг на пути к созданию собственных типов данных, в чем мы скоро убедимся.
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
Схожі підручники
- РЕГІОНАЛЬНА ЕКОНОМІКА. Тексти лекцій онлайн (частина 2)
- ЗМІСТ НАВЧАЛЬНОЇ ДИСЦИПЛІНИ ЗА ЗМІСТОВИМИ МОДУЛЯМИ
- Міжнародні економічні зв’язки України та її інтеграція в європейські та інші світові структури
- Конспект лекцій з курсу Введення у фінансову діяльність (частина 2)
- Українська мова за професійним спрямуванням. Навчальний посібник (частина 4)
- Р. ЛАФОРЕ ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ В C++ (4-Е ИЗДАНИЕ) (часть13) онлайн
