Определение классов. Конструкторы и деструкторы. Перегрузка функций и операторов.
8.1. Определение класса.
Классом называется структура данных и набор функций, которые обеспечивают доступ к элементам этой структуры. Данные класса называются его атрибутами, а функции – методами. Атрибуты и методы класса называются его членами. Синтаксически, класс определяется следующим образом:
class имя_класса
{
// члены класса
};
Например, определим класс четных чисел:
1 2 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 33 34 35 36 37 38 39 40 41 42 |
class even { int n; // конструкторы even( ); public: even(const int& _n); even(const even& _n); // функции even next( ) const; // следующее четное число even prev( ) const; // предыдущее четное число // унарные операторы even& operator+=(const even& e); even& operator-=(const even& e); even& operator*=(const even& e); // унарный + и – even& operator-(void); even& operator+(void); // операторы сравнения friend bool operator<(const even& e1, const even& e2); friend bool operator>(const even& e1, const even& e2); friend bool operator==(const even& e1, const even& e2); friend bool operator!=(const even& e1, const even& e2); friend bool operator<=(const even& e1, const even& e2); friend bool operator>=(const even& e1, const even& e2); // бинарные арифметические операторы friend even operator+(const even& e1, const even& e2); friend even operator-(const even& e1, const even& e2); friend even operator*(const even& e1, const even& e2); // операторы ввода-вывода friend istream& operator>>(istream& in, even& e); friend ostream& operator<<(ostream& out, const even& e); }; |
Определение класса вводит область видимости, которой принадлежат имена членов класса. Поэтому внутри функций членов класса можно обращаться к другим членам класса по именам. При доступе к членам класса вне области видимости этого класса используются операторы (.) и (->), как и в случае доступа к членам структуры.
После определения класса можно объявлять переменные, которые имеют тип этого класса, или другими словами принадлежат этому классу или являются экземплярами этого класса. В языке программирования С++ экземпляры класса называются объектами. Кроме того, над объектами можно выполнять функции, определенные в классе. Например,
#include <iostream.h>
#include “even.h”
int main( )
{
even a(2), b(0), c(0);
b = a.next( ); // b = 4
c = a + b; // c = 6
cout << “c = ” << c << endl;
return 1;
}
8.2. Спецификаторы доступа.
Для ограничения доступа к членам класса вне его области видимости используются спецификаторы доступа: public, protected и private, которые также называются метками и могут использоваться внутри класса произвольное число раз и в любом порядке.
Все члены класса, объявленные после метки public и расположенные до следующей метки, доступны как в классе, так и вне класса.
Все члены класса, объявленные после метки protected и расположенные до следующей метки, доступны как в классе, так и вне класса.
Все члены класса, объявленные после метки private и расположенные до следующей метки, доступны только внутри класса.
По умолчанию все члены класса, объявленного с помощью ключевого слова class, имеют спецификацию доступа private, а объявленные с помощью ключевых слов structure или union – спецификацию доступа public.
В нашем примере два члена класса, а именно, атрибут n и функция even объявлены закрытыми. Остальные члены класса открыты для программистов-пользователей класса. Функция even является конструктором по умолчанию, о котором будет более подробно рассказано далее.
7.4. Друзья класса.
Доступ к закрытым членам класса из другого класса или функции допускается только в том случае, если они объявлены как друзья класса. Объявление друга класса начинается с ключевого слова friend и может встречаться только внутри определения класса. В нашем примере друзьями класса объявлены операторы сравнения, бинарные арифметические операторы и операторы ввода-вывода.
8.3. Определение функций класса.
Функции класса определяются как обычные функции. Но, для того чтобы показать, что эти функции принадлежат классу, нужно перед именем функции записать имя класса, после которого символы “::”. Эти символы уточняют, что функция принадлежит классу и называются оператором разрешения области видимости. Если функция определяется внутри класса, то использование оператора разрешения области видимости не нужно. Например, определим функцию next( ):
even even::next( ) const // следующее четное число
{
return even(n + 2);
}
Спецификатор доступа const после списка параметров функции указывает на то, что эта функция не изменяет членов-данных класса even и поэтому может применяться к константным объектам этого класса.
8.4. Конструкторы и деструкторы.
Конструктором класса называется функция-член класса, которая всегда вызывается компилятором после создания объекта, которая заключается в распределения памяти под объект. Конструкторы используются для инициализации членов-данных класса и их имя всегда совпадает с именем класса. В нашем примере класс even имеет три конструктора: even( ), even(const int& _n) и even(const even& e).
Конструктор even( ) не имеет параметров и называется конструктором по умолчанию. В нашем случае этот конструктор объявлен в разделе личных членов класса, что запрещает его использование программистом. Это сделано потому, что нам неизвестно как инициализировать четное число по умолчанию.
Конструктор even(const int& _n) инициализирует четное число целым числом _n и имеет следующую реализацию:
even::even(const int& _n)
{
if (n % 2)
throw NotEven( ); // выбрасываем исключение: число не является четным
else
n = _n;
}
где исключение NotEven определено следующим образом:
struct NotEven { };
Конструктор, единственным параметром которого является объект того же класса, называется конструктором копирования. В нашем случае это конструктор even(const even& e), который имеет следующую реализацию:
even::even(const even& e): n(e.n) {}
В этом случае запись n(e.n) обозначает инициализацию атрибута n таким же атрибутом объекта e. Фактически запись n(e.n) является вызовом конструктора члена n, который принадлежит объекту класса even. В общем случае после двоеточия можно инициализировать произвольное количество атрибутов класса, перечисляя их через запятую. Это перечисление называется списком инициализации.
Деструктором называется функция член класса, которая автоматически вызывается компилятором перед удалением экземпляра класса из памяти. Имя деструктора совпадает с именем класса, перед которым дополнительно ставится символ “~” (тильда). Деструкторы используются для освобождения ресурсов, захваченных во время существования объекта. Например, во время своей жизни объект захватывал память, которую нужно при уничтожении объекта освободить. В нашем случае ресурсы не захватывались, поэтому деструкторы не нужны.
8.5. Перегрузка функций и операторов.
В языке С++ разрешается определять функции, которые имеют одинаковое название, но разные сигнатуры. Напомним, что сигнатурой называется список типов параметров функции. Такие функции называются перегруженными. Перегружать функции, которые отличаются только возвращаемым значением нельзя. Например, перегрузим функцию вычисления абсолютной величины числа:
int abs(int n);
double abs(double d);
Перегрузка функций позволяет давать похожим функциям одинаковые осмысленные имена.
В языке программирования С++ операторы также рассматриваются как функции, которые имеют следующий синтаксис:
возвращаемый_тип operator имя_оператора(список_параметров)
где operator – ключевое слово. Поэтому операторы также как и функции можно перегружать. В нашем примере перегружаются унарные и бинарные арифметические операторы, а также бинарные операторы сравнения и ввода-вывода. Например, перегрузим унарный и бинарный операторы сложения:
even& even::operator+=(const even& e)
{
n += e.n;
return *this;
}
even operator+(const even& e1, const even& e2)
{
return even(e1.n + e2.n);
}
Отметим следующую разницу между унарным и бинарным операторами сложения. Унарный оператор возвращает ссылку, а бинарный – копирует объект в вызывающую программу. Так происходит потому, что унарный оператор возвращает существующий объект, а именно, объект, к которому применяется операция +=, а бинарный оператор возвращает новый объект, который создается внутри тела оператора. Ключевое слово this обозначает указатель на объект класса, к которому применяется функция. Можно рассматривать this как неявный параметр этой функции. Запись *this обозначает разыменование этого указателя, поэтому функция += и возвращает объект класса, к которому применяется метод.
Теперь перегрузим оператор для вывода четного числа:
ostream& operator<<(ostream& out, const even& e)
{
return out << e.n;
}