Описание классов и объектов класса
Класс - абстрактный тип данных, определяемый пользователем. Он представляет собой модель реального объекта в виде данных и функций для работы с ними.
Элементы класса:
· Поля (компонентные данные, данные-члены) – данные класса;
· Методы (компонентные функции,функции-члены) – функции класса для работы с данными.
Синтаксис описания класса:
class <имя> {
[ private:] <описание скрытых элементов>
public: <описание доступных элементов>
} ;
private и public - спецификаторы доступа, управляющие видимостью элементов класса. После спецификатора private описываются элементы, видимые только внутри класса. Этот вид доступа принят в классе по умолчанию. После спецификатора public описывается интерфейс класса.
Действие спецификатора распространяется до следующего спецификатора или до конца класса. Можно задавать несколько секций private и public, порядок их следования значения не имеет.
Поля класса:
· могут иметь любой тип, кроме типа этого же класса (но могут быть указателями или ссылками на него);
· могут быть описаны с модификатором const (они инициализируются только один раз с помощью конструктора и не могут изменяться);
· могут быть описаны с модификатором static (статические поля), но не как auto, extern и register.
· Не могут инициализироваться при описании.
Классы могут быть глобальными (объявленными вне любого блока) и локальными (объявленными внутри блока, например, функции или другого класса).
Особенности локального класса:
· внутри можно использовать типы, статические (static) и внешние (extern) переменные, внешние функции и элементы перечислений из области, в которой он описан; запрещается использовать автоматические переменные из этой области;
· не может иметь статических элементов;
· его методы могут быть описаны только внутри класса;
· если один класс вложен в другой класс, они не имеют каких-либо особых прав доступа к элементам друг друга и могут обращаться к ним только по общим правилам.
Пример: создать класс, моделирующий персонаж компьютерной игры. Требуется задать его свойства и поведение.
class monstr{
int health, ammo;
public:
monstr(int he = 100, int am = 10){ health = he; ammo = am;}
void draw(int x, int y, int scale, int position);
int get_health(){return health;}
int get_ammo(){return ammo;}
};
Значения скрытых полей health и ammo нельзя получить извне, а только с помощью методов get_health() и get_ammo().
Все методы класса имеют непосредственный доступ к его скрытым полям, т.е. тела функций класса входят в область видимости private-элементов класса.
Если тело метода определено внутри класса, он является встроенным (в примере методы monstr, get_health, get_ammo). Как правило, встроенными делают короткие методы.
Если внутри класса записано только объявление метода (в примере метод draw), он должен быть определен в другом месте программы с помощью операции доступа к области видимости:
void monstr::draw(int х, int у, int scale, int position){
/ * тело метода * /
}
Метод можно определить как встроенный и вне класса с помощью директивы inline (как и для обычных функций, она носит рекомендательный характер):
inline int monstr::get_ammo(){
return ammo;
}
Метод, имя которого совпадает с именем класса – конструктор. Он вызывается автоматически при создании объекта класса и инициализирует его. Автоматический вызов конструктора позволяет избежать ошибок, связанных с использованием неинициализированных переменных.
Типы данных struct и union являются видами класса.
Описание объектов
Конкретные переменные типа «класс» называются экземплярами класса, или объектами.Время жизни и видимость объектов зависит от вида и места их описания и подчиняется общим правилам С++:
monstr Vasia; // Объект класса monstr с параметрами по умолчанию.
monstr Super(200, 300); // Объект с явной инициализацией
monstr stado[100]; // Массив объектов с параметрами по умолчанию
monstr *beavis = new monstr (10); // Динамический объект (второй
// параметр по умолчанию)
monstr &butthead = Vasia; // Ссылка на объект
При создании каждого объекта выделяется память, достаточная для хранения всех его полей, и автоматически вызывается конструктор, выполняющий их инициализацию. Методы класса не тиражируются. При выходе объекта из области действия он уничтожается, при этом автоматически вызывается деструктор.
Для доступа к элементам объекта используются операция . (точка) при обращении к элементу через имя объекта и операция -> при обращении через указатель:
int n = Vasia.get_ammo();
stado[5].draw;
cout << beavis->get_health();
Обратиться таким образом можно только к элементам со спецификаторам public. Получить или изменить значения элементов со спецификатором private можно только через обращение к соответствующим методам.
Можно создать константный объект, значения полей которого изменять запрещается. К нему должны применяться только константные методы:
Константный метод:
· объявляется с ключевым словом const после списка параметров,
· не может изменять значения полей класса,
· может вызывать только константные методы,
· может вызываться для любых (не только константных) объектов.
class monstr{
…
int get_health() const {return health;}
};
const monstr Dead(0,0); // Константный объект
cout << Dead.get_health();
Рекомендуется описывать как константные те методы, которые предназначены для получения значений полей.
Тема 2.3
Указатель this
Каждый объект содержит свой экземпляр полей класса. Методы класса находятся в памяти в единственном экземпляре и используются всеми объектами совместно, поэтому необходимо обеспечить работу методов с полями того объекта, для которого они были вызваны. Это обеспечивается передачей в функцию скрытого параметра this, в котором хранится константный указатель на вызвавший функцию объект.
Указатель this неявно используется внутри метода для ссылок на элементы объекта. В явном виде этот указатель применяется в основном для возвращения из метода указателя (return this;) или ссылки (return *this;) на вызвавший объект.
Пример: добавим в класс monstr в секцию public метод, возвращающий ссылку на объект с наибольшим значением поля health для двух объектов, один из которых вызывает метод, а другой передается ему в качестве параметра).
monstr & the_best(monstr &М){
if( health > М.health) return *this;
return M;
}
…
monstr Vasia(50), Super(200);
monstr Best = Vasia.the_best(Super); // Новый объект Best инициализируется // значениями полей объекта с
// наибольшим значением поля health, // т.е. Super.
Указатель this можно также применять для идентификации поля класса в том случае, когда его имя совпадает с именем формального параметра метода. Другой способ идентификации поля использует операцию доступа к области видимости:
void cure(int health, int ammo) {
this -> health += health; // Использование this
monstr:: ammo += ammo; // Использование операции ::
}
Тема 2.4
Конструкторы
Основные свойства конструкторов:
1. Конструктор не возвращает значение (даже типа void),
2. Нельзя получить указатель на конструктор,
3. Класс может иметь несколько конструкторов с разными параметрами для разных видов инициализации (при этом используется механизм перегрузки).
4. Конструктор, вызываемый без параметров, называется конструктором по умолчанию.
5. Параметры конструктора могут иметь любой тип, кроме этого же класса.
6. Можно задавать значения параметров по умолчанию. Их может содержать только один из конструкторов.
7. Если в классе не определен ни один конструктор, компилятор создает его автоматически. Такой конструктор вызывает конструкторы по умолчанию для полей класса и конструкторы по умолчанию базовых классов. Если класс содержит константы или ссылки, при попытке создания объекта класса будет выдана ошибка, поскольку их необходимо инициализировать конкретными значениями, а конструктор по умолчанию этого не делает.
8. Конструкторы не наследуются.
9. Конструкторы нельзя описывать с модификаторами const, virtual и static.
10. Конструкторы глобальных объектов вызываются до вызова функции main. Локальные объекты создаются, как только становится активной область их действия. Конструктор запускается также при создании временного объекта (например, при передаче объекта из функции).
11. Конструктор вызывается при употреблении синтаксических конструкций вида:
имя_класса имя_объекта [(список параметров)]; // Список параметров не // должен быть пустым
имя_класса (список параметров); // Создается объект без имени
// (список может быть пустым)
имя_класса имя_объекта = выражение; // Создается объект без имени
// и копируется
Примеры:
monstr Super(200, 300), Vasia(50), Z;
monstr X = monstr(l000);
monstr Y = 500;
В первом операторе создаются три именованных объекта. Значения не указанных параметров устанавливаются по умолчанию.
Во втором операторе создается безымянный объект со значением параметра health равным 1000 (значение второго параметра устанавливается по умолчанию). Этот объект копируется в память, выделенную под объект X.
В последнем операторе выполняются аналогичные действия (health = 500), но такая форма создания объекта возможна только в том случае, если для его инициализации допускается задать один параметр.
Примеркласса с несколькими конструкторами (в класс monstr добавления поля skin и name).
enum color {red, green, blue}; // Возможные значения цвета
class monstr{
int health, ammo;
color skin;
char *name;
public:
monstr(int he = 100, int am =10);
monstr(color sk);
monstr(char * nam);
int get_health(){return health;}
int get_ammo(){return ammo;}
…
};
//-----------------------------------------------
monstr::monstr(int he, int am){
health = he; ammo = am; skin = red; name = 0;
}
//-----------------------------------------------
monstr::monstr(color sk){
switch (sk){
case red: health = 100; ammo = 10; skin = red; name = 0; break;
case green: health = 100; ammo = 20; skin = green; name = 0; break;
case blue: health = 100; ammo = 40; skin = blue; name = 0; break;
}
}
//-----------------------------------------------
monstr::monstr(char * nam){
name = new char [strlen(nam) + 1]; // К длине строки добавляется 1 для
// хранения нуль-символа
strcpy(name, nam);
health = 100; ammo =10; skin = red;
}
…
//-----------------------------------------------
monstr * m = new monstr ("Ork");
monstr Green (green);
Первый конструктор является конструктором по умолчанию, т.к. его можно вызвать без параметров. Объекты класса monstr можно инициализировать различными способами, требуемый конструктор будет вызван в зависимости от списка параметров.
При задании нескольких конструкторов следует соблюдать правила, позволяющие компилятору распознать нужный вариант функции (аналогично перегруженным функциям).
Перегружать можно также другие методы класса.
Способы инициализации полей в конструкторе:
· присваивание полям значений формальных параметров (в рассмотренном выше примере)
· с помощью списка инициализаторов, расположенных после двоеточия между заголовком и телом конструктора. Для каждого поля в скобках указывается инициализирующее значение, которое может быть выражением.
monstr::monstr(in the, int am);
health (he), ammo (am), skin (red), name (0){}
Данный способ используется при инициализации полей-констант, полей-ссылок и полей-объектов. В последнем случае будет вызван конструктор, соответствующий указанным в скобках параметрам.
Конструктор не может возвратить значение, чтобы сообщить об ошибке во время инициализации. Для этого можно использовать механизм обработки исключительных ситуаций.
Конструктор копирования – специальный вид конструктора, получающий в качестве единственного параметра указатель на объект этого же класса:
T::T(const Т&) { …/ * Тело конструктора */ }
Т – имя класса.
Он вызывается, когда новый объект создается путем копирования существующего:
· при описании нового объекта с инициализацией другим объектом,
· при передаче объекта в функцию по значению,
· при возврате объекта из функции,
· при обработке исключений.
Если не указан ни один конструктор копирования, компилятор создает его автоматически. Такой конструктор выполняет поэлементное копирование полей. Если класс содержит указатели или ссылки, это, скорее всего, будет неправильным, поскольку и копия, и оригинал будут указывать на одну и ту же область памяти.
Пример:конструктора копирования для класса monstr. Для поля name, содержащего указатель на строку символов, конструктор копирования должен выделять память под новую строку и копировать в нее исходную.
monstr::monstr(const monstr &М){
if (M.name){
name = new char [strlen(M.name) + 1];
strcpy(name, M.name);
}
else name = 0;
health = M.health; ammo = M.ammo; skin = M.skin;
}
…
monstr Vasia (blue);
monstr Super = Vasia; // вызывается конструктор копирования
monstr *m = new monstr ("Orc");
monstr Green = *m; // вызывается конструктор копирования
Конструктор преобразования - конструктор класса, принимающий один параметр какого-либо типа, кроме типа данного класса. Он осуществляет преобразование типа параметра в тип этого класса.
Тема 2.5
Деструкторы
Деструктор – это особый вид метода, применяющийся для освобождения памяти, занимаемой объектом. Его имя начинается со знака ~ (тильда), далее следует имя класса.
Деструктор для рассматриваемого ранее примера:
monstr::~monstr() {delete [ ] name;}
Если деструктор явным образом не определен, компилятор автоматически создает пустой деструктор.
Описывать в классе деструктор явным образом требуется в случае, когда объект содержит указатели на память, выделяемую динамически - иначе при уничтожении объекта память, на которую ссылались его поля-указатели, не будет помечена как свободная. Указатель на деструктор определить нельзя.
Деструктор:
· не имеет аргументов и возвращаемого значения;
· не может быть объявлен как const или static;
· не наследуется;
· может быть виртуальным.
Деструктор вызывается автоматически, когда объект выходит из области видимости:
· для локальных объектов — при выходе из блока, в котором они объявлены;
· для глобальных — при выходе из функции main;
· для объектов, заданных через указатели — при использовании операции delete (вызывается неявно). При выходе из области действия указателя на объект автоматический вызов деструктора объекта не производится.
Деструктор можно вызвать явным образом:
monstr *m; …
m -> ~monstr();
Это необходимо для объектов, которым с помощью перегруженной операции new выделялся конкретный адрес памяти. Без необходимости явно вызывать деструктор объекта не рекомендуется.
Тема 2.6