}
Int main()
{
myclass ob;
ob.setab(5); // Устанавливаем члены данных ob.a и ob.b.
cout << "Объект ob после вызова функции setab(5): ";
cout << ob.geta() << ' ';
cout << ob.b; // К члену b можно получить прямой доступ, поскольку он является public-членом.
cout << '';
ob.b = 20; // Член b можно установить напрямую, поскольку он является public-членом.
cout << "Объект ob после установки члена ob.b=20: ";
cout << ob.geta() <<' ';
cout << ob.b;
cout << '';
ob.reset();
cout << "Объект ob после вызова функции ob.reset(): ";
cout << ob.geta() << ' ';
cout << ob.b;
cout << '';
return 0;
}
При выполнении этой программы получаем следующие результаты.
Объект ob после вызова функции setab(5): 5 25
Объект ob после установки члена ob.b=20: 5 20
Объект ob после вызова функции ob.reset(): 0 0
Теперь рассмотрим, как осуществляется доступ к членам класса myclass. Прежде всего обратите внимание на то, что для присвоения значений переменным а и b в функции setab() используются следующие строки кода.
а = i; // прямое обращение к переменной а
b = i*i; // прямое обращение к переменной b
Поскольку функция setab() является членом класса, она может обращаться к членам данных а и b того же класса непосредственно, без явного указания имени объекта (и не используя оператор "точка"). Как упоминалось выше, функция-член всегда вызывается для определенного объекта (а коль вызов состоялся, объект, стало быть, известен). Таким образом, в теле функции-члена нет необходимости указывать объект вторично. Следовательно, ссылки на переменные а и b будут применяться к копиям этих переменных, относящимся к вызывающему объекту.
Теперь обратите внимание на то, что переменная b — открытый (public) член класса myclass. Это означает, что к b можно получить доступ из кода, определенного вне тела класса myclass. Следующая строка кода из функции main(), при выполнении которой переменной b присваивается число 20, демонстрирует реализацию такого прямого доступа.
ob.b = 20; // К члену b можно получить прямой доступ.
// поскольку он является public-членом.
Поскольку эта инструкция не принадлежит телу класса myclass, то к переменной b возможен доступ только с использованием конкретного объекта (в данном случае объекта ob) и оператора "точка".
Теперь обратите внимание на то, как вызывается функция-член reset() из функции main().
ob.reset();
Поскольку функция reset() является открытым членом класса, ее также можно вызвать из кода, определенного вне тела класса myclass, и посредством конкретного объекта (в данном случае объекта ob).
Наконец, рассмотрим код функции reset(). Тот факт, что она является функцией-членом, позволяет ей непосредственно обращаться к другим членам того же класса, не используя оператор "точка" или конкретный объект. В данном случае она вызывает функцию-член setab(). И снова-таки, поскольку объект уже известен (он используется для вызова функции reset()), нет никакой необходимости указывать его еще раз.
Здесь важно понять следующее: когда доступ к некоторому члену класса происходит извне этого класса, его необходимо квалифицировать (уточнить) с помощью имени конкретного объекта. Но код самой функции-члена может обращаться к другим членам того же класса напрямую.
На заметку. Не стоит волноваться, если вы еще не почувствовали в себе уверенность в вопросах получения доступа к членам класса. Небольшое беспокойство при освоении этой темы — обычное явление для начинающих программистов. Смело продолжайте читать книгу, рассматривая как можно больше примеров, и тема доступа к членам класса вскоре станет такой же простой, как таблица умножения!
Конструкторы и деструкторы
Конструктор — это функция, которая вызывается при создании объекта.
Как правило, некоторую часть объекта, прежде чем его можно будет использовать, необходимо инициализировать. Например, рассмотрим класс queue (он представлен выше в этой главе). Прежде чем класс queue можно будет использовать, переменным rloc и sloc нужно присвоить нулевые значения. В данном конкретном случае это требование выполнялось с помощью функции init(). Но, поскольку требование инициализации членов класса весьма распространено, в C++ предусмотрена реализация этой возможности при создании объектов класса. Такая автоматическая инициализация выполняется благодаря использованию конструктора.
Конструктор — это специальная функция, которая является членом класса и имя которой совпадает с именем класса. Вот, например, как стал выглядеть класс queue после переделки, связанной с применением конструктора для инициализации его членов.
// Определение класса queue.
class queue {
int q[100];
int sloc, rloc;
public:
queue(); // конструктор
void qput(int i);
int qget();
};
Обратите внимание на то, что в объявлении конструктора queue() отсутствует тип возвращаемого значения. В C++ конструкторы не возвращают значений и, следовательно, нет смысла в указании их типа. (При этом нельзя указывать даже тип void.)
Теперь приведем код функции queue().
// Определение конструктора.
Queue::queue()
{
sloc = rloc = 0;
cout << "Очередь инициализирована.";
}
В данном случае при выполнении конструктора выводится сообщение Очередь инициализирована., которое служит исключительно иллюстративным целям. На практике же в большинстве случаев конструкторы не выводят никаких сообщений.
Конструктор объекта вызывается при создании объекта. Это означает, что он вызывается при выполнении инструкции объявления объекта. Конструкторы глобальных объектов вызываются в самом начале выполнения программы, еще до обращения к функции main(). Что касается локальных объектов, то их конструкторы вызываются каждый раз, когда встречается объявление такого объекта.
Деструктор — это функция, которая вызывается при разрушении объекта.
Дополнением к конструктору служит деструктор. Во многих случаях при разрушении объекту необходимо выполнить некоторое действие или даже некоторую последовательность действий. Локальные объекты создаются при входе в блок, в котором они определены, и разрушаются при выходе из него. Глобальные объекты разрушаются при завершении программы. Существует множество факторов, обуславливающих необходимость деструктора. Например, объект должен освободить ранее выделенную для него память. В C++ именно деструктору поручается обработка процесса дезактивизации объекта. Имя деструктора совпадает с именем конструктора, но предваряется символом "~" Подобно конструкторам деструкторы не возвращают значений, а следовательно, в их объявлениях отсутствует тип возвращаемого значения.
Рассмотрим уже знакомый нам класс queue, но теперь он содержит конструктор и деструктор. (Справедливости ради отметим, что классу queue деструктор, по сути, не нужен, а его наличие здесь можно оправдать лишь иллюстративными целями.)
// Определение класса queue.
class queue {
int q[100];
int sloc, rloc;
public:
queue(); // конструктор
~queue(); // деструктор
void qput(int i);
int qget();
};
// Определение конструктора.
Queue::queue()
{
sloc = rloc = 0;
cout << "Очередь инициализирована.";
}
// Определение деструктора.
queue::~queue()
{
cout << "Очередь разрушена.";
}
Вот как выглядит новая версия программы реализации очереди, в которой демонстрируется использование конструктора и деструктора.
// Демонстрация использования конструктора и деструктора.
#include <iostream>
using namespace std;
// Определение класса queue.
class queue {
int q[100];
int sloc, rloc;
public:
queue(); // конструктор
~queue(); // деструктор
void qput(int i);
int qget();
};
// Определение конструктора.
Queue::queue()
{
sloc = rloc = 0;
cout << "Очередь инициализирована.";
}
// Определение деструктора.
queue::~queue()
{
cout << "Очередь разрушена.";
}
// Занесение в очередь целочисленного значения.
Void queue::qput(int i)
{
if(sloc==100) {
cout << "Очередь заполнена.";
return;
}
sloc++;
q[sloc] = i;
}
// Извлечение из очереди целочисленного значения.
Int queue::qget()
{
if(rloc == sloc) {
cout << "Очередь пуста.";
return 0;
}
rloc++;
return q[rloc];
}
Int main()
{
queue a, b; // Создание двух объектов класса queue.
a.qput(10);
b.qput(19);
a.qput(20);
b.qput(1);
cout << a.qget() << " ";
cout << a.qget() << "";
cout << b.qget() << " ";
cout << b.qget() << "";
return 0;
}