Объектно-ориентированное программирование — Класс как обобщение структуры
- Объектно-ориентированное программирование
- Локальные и глобальные переменные
- Подпрограммы и их аргументы
- Определение данных
- Операторы динамического распределения памяти
- Перегрузка функций и операций
- Правила составления перегружаемых функций и операций
- Класс как обобщение структуры
- Определение первичного класса
- Перегрузка операций
- Конструкторы
- Список инициализации
- Деструктор
- Дружественные классы
- Статические элементы класса
- Шаблоны функций
Объекты и классы
Классом называется набор типизированных данных и функций, объединенных в новый тип данных. В отличие от структуры, типизированные данные и функции имеют различные уровни доступа. Переменная, объявленная в программе, имеющая определенный классом тип данных, называется объектом этого класса. Таким образом, класс — это структура, состоящая из некоторых переменных (и констант) и функций, а объект — это область памяти, которую занимает структура при ее объявлении. Для создания объектов предусмотрены принадлежащие классу функции, заполняющие поля объектов. Эти функции называются конструкторами. При удалении объектов вызываются функции, принадлежащие классу и предназначенные для освобождения памяти, — деструкторы.
Класс как обобщение структуры
Выше мы дали определение класса, как конструкции, состоящей из полей и функций. В частности, этому определению удовлетворяет структура. В действительности, структура в Си++ реализована как класс, все поля которой, по умолчанию, общедоступны в том смысле, что доступ к ним осуществляется через имена имя_структуры.поле, имя_структуры.функция(аргументы), или указатели указатель->поле, указатель->функция(аргументы).
Простейшим образом класс можно определить с помощью конструкции:
где первая пара фигурных скобок обозначает альтернативный выбор одного из ключевых слов, а вторая пара включает в себя поля и имена функций, которые принадлежат классу. Такие функции называются составными функциями класса. Заключенный в фигурные скобки список компонент называется телом класса. Определение тела класса заканчивается точкой с запятой.
Пример 1. Будем использовать ключевое слово struct для определения класса двумерного вектора, для которого определены функции ввода и вывода данных, составляющих объект.
#include
#include
// Класс вектор
struct Vector
{
double x, y; // Координаты вектора
// Функция вывода на экран координат вектора
void get()
{
cout<<"x="<
cin>>x>>y;
}
};
void main()
{
clrscr(); // Очистка экрана
Vector v, w[2]; // Определение векторов
v.put(); w[0].put(); w[1].put(); // Ввод координат векторов
// Вывод координат векторов
cout<<"\nКоординаты вектора v: ";
v.get();
cout<<"Координаты вектора w[0]: ";
w[0].get();
cout<<"Координаты вектора w[1]: ";
w[1].get();
getch(); // Ожидание нажатия клавиши
}
Результаты работы программы
Введите через пробел координаты вектора (x и y): 12.4 3.56
Введите через пробел координаты вектора (x и y): 2.34 5.6
Введите через пробел координаты вектора (x и y): 7 8.02
Координаты вектора v: x=12.4 y=3.56
Координаты вектора w[0]: x=2.34 y=5.6
Координаты вектора w[1]: x=7 y=8.02
Тело составной функции может быть определено вне класса. В таком случае для этой функции указывается имя класса, членом которой она является:
тип_возвращаемого_значения имя_класса::функция(аргументы) {…}
Следует помнить, что указатель на объект, к которому принадлежит составная функция, определяется с помощью ключевого слова this. В частности, в предшествующем примере в функциях put() и get() переменные x и y будут равны (*this).x и (*this).y.
Пример 2. Рассмотрим подпрограмму перегрузки операции присваивания для структуры, состоящей из строки и ее длины. В теле класса эта функция объявлена как
str& operator = (const str&);
она будет возвращать адрес объекта, полученного после присваивания. Это позволит применять цепочки присваиваний, например, str1 = str2 = str3. Аргумент функции сделаем ссылочным, чтобы избежать копирования всего объекта в стек при вызове операции присваивания. В стек теперь будет сохраняться адрес объекта.
#include
#include
#include
// Класс строка
struct Str
{
char *s; // Указатель на строку
int len; // Длина строки
void init(const char*); // Функция инициализации строки
Str operator = (const Str); // Перегрузка операции =
};
// Перегрузка операции =
Str Str::operator = (const Str st)
{
len = st.len; // Выяснение длины новой строки
delete s; // Удаление старого содержимого
s = new char[len + 1]; // Выделение памяти под новую строку strcpy(s, st.s); // Копирование строки
return *this; // Возвращение полученной строки по значению
}
// Функция инициализации строки
void Str::init(const char* s)
{
len = strlen(s); // Выяснение длины строки
Str::s = new char[len + 1]; // Выделение памяти под строку strcpy(Str::s, s); // Копирование строки
}
void main()
{
clrscr(); // Очистка экрана
Str str1, str2, str3; // Создание строк
str1.init("Пирамида"); // Инициализация первой строки
str3 = str2 = str1; // Присваивание значения первой строки
// остальным двух строкам
cout<<"Объект str3 = " << str3.s << '\n'; // Вывод третьей строки
getch(); // Ожидание нажатия клавиши
}
Результаты работы программы
Объект str3 = Пирамида
В этом примере мы столкнулись со следующей проблемой: подпрограмма init имеет формальный параметр с именем s, совпадающим с именем строки s в классе Str. Для того чтобы отличать имя строки в классе, применяется модификатор расширения области видимости «::». В данном случае к строке класса применяется обращение Str::s.