Вызов метода и доступ к элементам класса
Методы выполняют операции над объектами класса. Поэтому при обращении к функции Getx необходимо указать, с каким объектом класса Point следует работать. Если бы Getx была обычной функцией языка С (или не методом С++) эта проблема не возникла бы: функция вызывалась бы простым обращением к Getx().
Чтобы вызвать метод класса в той части программы, которая не является частью класса, надо использовать имя объекта и операцию точка (.) для доступа к элементу класса, аналогично обращению к элементам структуры языка С, согласно форме:
имя_объекта . имя_метода (список аргументов)
имя_объекта . имя_элемента_данных
Например, пусть описаны объекты класса:
Point p1, p2, *pp;
Тогда возможные обращения к элементам класса имеют вид:
p1. X; // доступ к элементу Х объекта р1
p2.Y; // доступ к элементу Y объекта р2
p1. Getx(); // вызов метода Getx()
С другой стороны, метод класса может вызвать другой метод того же класса или использовать данные-элементы класса непосредственно, не используя операцию точка.
Вывод. Операцию точка надо использовать только тогда, когда метод класса вызывается в функциях, не являющихся элементами класса.
Если имеется указатель на объект типа Point, следует применять операцию вызова метода посредством указателя, используя операцию "стрелка"
( ->): pp->Getx().
Рассмотрим программу, которая иллюстрирует особенности языка С++, обсуждавшиеся выше.
Пример 9.
Программа моделирования работы класса "очередь" с фиксированной длиной очереди.
#include<iostream.h>
#include<conio.h>
const int sizeq= 5 ; // размер очереди
class Queue // класс "очередь"
{ int q[sizeq]; // массив очереди
int head, tail; // начало и конец очереди
public: // прототипы методов класса:
void init(void); // инициализация очереди
void qput(int); // постановка в очередь
int qget(void); // выход из очереди
void show(); // вывод данных очереди
};
// описание методов класса:
void Queue::init(void) // метод инициализации очереди
{ tail=head=0; // исходное обнуление очереди
for(int i=0; i<sizeq; i++)
q[i]=0;
}
void Queue::qput(int i) // метод постановки в очередь
{ if(tail==sizeq) // если все места заняты, то сообщение
{ cout << "Очередь полна ";
return; // возврат из функции
}
q[tail++]=i; // увеличение очереди
}
int Queue::qget(void) // метод выхода из очереди
{ if(head==tail) // если достигнут конец очереди, то сообщение
{
cout << "Очередь пуста ";
return 1111; // возврат какого-либо значения
}
int i=q[head]; // значение уходящего элемента очереди
q[head++]; // сдвиг начала очереди к концу
return i; // возврат значения элемента очереди
}
void Queue::show() // метод вывода значений очереди
{cout<<"Значения: head="<<head<<" tail="<<tail<<"\nЭлементы очереди: ";
for(int i=0; i<sizeq; i++)
cout<<q[i]<<" ";
cout<<"\n";
}
int main() // главная функция
{ int i;
clrscr(); // чистка экрана результатов
cout<<"Создаем два объекта a, b:"<<"\n";
Queue a, b; // созданы объекты-очереди a, b
cout<<"Инициализация очереди а: ";
a.init();
a.show(); // вывод данных очереди а
// заполнение очереди а значениями:
a.qput(7);
a.qput(9);
a.qput(11);
cout<<"Очередь а: ";
a.show(); // вывод данных очереди а
cout<<"Вывод данных очереди а в цикле:\n";
for(i=0; i<sizeq; i++) // цикл вывода из очереди а
cout << a.qget() << " ";
cout<<"\n";
a.show(); // вывод данных очереди а
cout<<"\nИнициализация очереди b: ";
b.init();
b.show(); // вывод данных очереди b
for(i=0; i<sizeq+1; i++) // цикл заполнения очереди b
b.qput(i*i);
cout<<"\nОчередь b: ";
b.show(); // вывод данных очереди b
cout<<"Вывод данных очереди b в цикле:\n";
for(i=0; i<sizeq+1; i++) // вывод данных в цикле
cout<<b.qget()<<"";
cout<<"\n";
b.show(); // вывод данных очереди b
getch(); // задержка экрана результатов
return 0; // нормальное окончание программы
}
Результаты программы:
Создаем два объекта a, b:
Инициализация очереди а: Значения: head=0 tail=0
Элементы очереди: 0 0 0 0 0
Очередь а: Значения: head=0 tail=3
Элементы очереди: 7 9 11 0 0
7 9 1 Очередь пуста 1111
Значения: head=3 tail=3
Элементы очереди: 7 9 11 0 0
Инициализация очереди b: Значения: head=0 tail=0
Элементы очереди: 0 0 0 0 0
Очередь полна
Очередь b: Значения: head=0 tail=5
Элементы очереди: 01 4 9 16
Вывод данных очереди b в цикле: 01 4 9 16 Очередь пуста 1111
Значения: head=5 tail=5
Элементы очереди: 01 4 9 16
Особенностью этого примера является то, что функция main() стоит не на первом месте, как в программах на языке С. Обычно в программах С++ методы классов определяются перед main(). По мере возрастания объема программ объявления классов следует выделять в файлы заголовков, а определения методов класса располагать в отдельно компилируемых исходных файлах С++.
Конструкторы и деструкторы
При создании объекты обычно инициализируются. Например, в классе очереди Queue (пример 9) это было реализовано функцией init(). Среди функций класса имеются специальные методы, которые определяют особенности создания, инициализации, копирования и уничтожения объектов данного класса. Их называют конструкторы и деструкторы. Как и обычные методы, конструкторы и деструкторы могут описываться в пределах или вне класса.
Конструктор это метод, основной целью которого является инициализация переменных объекта данного класса или распределение памяти для их хранения (constructor – создатель объекта). С++ предоставляет возможность делать это автоматически при объявлении объекта, то есть при его создании. С объектом всегда связано либо явное, либо не явное выполнение конструктора. Конструктор вызывается в тот момент, когда создается объект класса, то есть ему выделяется место в памяти. Нельзя вызвать конструктор в явном виде в программе как обычную функцию. Он вызывается явно компилятором при создании объекта и неявно при выполнении оператора new, применяемого к объекту, а также при копировании объекта данного класса. Если конструктор не описан, то компилятор создает автоматически конструктор по умолчанию.
Конструктор имеет то же имя, что и класс, в котором он определен, но не может иметь тип возвращаемого значения, в том числе void. Конструктор в большинстве случаев никаких сообщений не выдает. Он может иметь параметры, в том числе по умолчанию, может быть перегруженной функцией, то есть класс может иметь несколько разных конструкторов. Конструктор, не имеющий аргументов, называется конструктором по умолчанию.
Пример 10.
Представим объявление и описание конструктора в классе Queue.
class Queue // класс "очередь"
{ int q[sizeq]; // массив очереди
int head, tail; // начало и конец очереди
public: // прототипы методов класса:
Queue (void); // это объявление конструктора в классе
void qput(int); // постановка в очередь
int qget(void); // выход из очереди
void show(); // вывод данных очереди
};
// описание (определение) конструктора Queue вне класса:
Queue :: Queue (void)
{ tail=head=0; // исходное обнуление очереди
for(int i=0; i<sizeq; i++)
q[i]=0;
}
Метод класса деструктор (destructor) разрушает объект данного класса и может вызываться явно с помощью оператора delete, либо неявно, выполняя действия перед окончанием работы объекта (освобождение памяти, восстановление экрана, закрытие файлов и т. д.). Если деструктор не объявлен явно, компилятор генерирует его автоматически.
Деструктор имеет имя класса с символом тильда (~) перед ним. В отличие от конструктора он не может получать аргументы и быть перегруженной функцией.
Для статических объектов память распределяется компилятором: конструктор вызывается перед main, а деструктор — после main. Деструктор вызывается неявно, когда автоматическая (auto) переменная выходит из области действия. Например, объект является локальным внутри функции, и функция возвращает управление. В случае глобальных переменных деструкторы вызываются как часть процедуры выхода после main.
При создании динамического объекта с помощью оператора new для удаления объекта, хранящегося в динамической памяти, явно используется оператор delete.
Деструкторы вызываются строго в обратной последовательности вызова соответствующих конструкторов.