Презентацию к лекции Вы можете скачать здесь.
В окончательном виде любая программа представляет собой набор инструкций процессора. Все, что написано на любом языке программирования - более удобная, упрощенная запись этого набора инструкций, облегчающая написание,
С ростом объема программы становится невозможным удерживать в памяти все детали, и становится необходимым структурировать информацию, выделять главное и отбрасывать несущественное. Этот процесс называется повышением степени
Для
Следующий шаг - описание собственных типов данных, позволяющих структурировать и группировать информацию, представляя ее в более естественном виде. Например, все разнородные сведения, относящиеся к одному виду товара на складе, можно представить с помощью одной структуры.
Для работы с собственными типами данных требуются специальные функции. Естественно сгруппировать их с описанием этих типов данных в одном месте программы, а также по возможности отделить от ее остальных частей. При этом для использования этих типов и функций не требуется полного знания того, как именно они написаны - необходимы только описания интерфейсов. Объединение в модули описаний типов данных и функций, предназначенных для работы с ними, со скрытием от пользователя модуля несущественных деталей является дальнейшим развитием структуризации программы.
Все три описанных выше метода повышения
Введение понятия класса является естественным развитием идей модульности. В классе структуры данных и функции их обработки объединяются. Класс используется только через его интерфейс - детали реализации для пользователя класса не существенны.
Идея классов отражает строение объектов реального мира - ведь каждый предмет или процесс обладает набором характеристик или отличительных черт, иными словами, свойствами и поведением. Программы в основном предназначены для моделирования предметов, процессов и явлений реального мира, поэтому удобно иметь в языке программирования
Класс является типом данных, определяемым пользователем. В классе задаются свойства и поведение какого-либо предмета или процесса в виде полей данных (аналогично структуре) и функций для работы с ними. Создаваемый тип данных обладает практически теми же свойствами, что и стандартные типы (напомню, что тип задает внутреннее
Существенным
Идея классов является основой и , но в то время не получили широкого применения из-за трудностей освоения и низкой С++ эти концепции реализованы эффективно и
Идеи
Конкретные переменные типа данных "класс" называются экземплярами класса, или объектами. Объекты взаимодействуют между собой, посылая и получая сообщения. Сообщение - это запрос на выполнение действия, содержащий набор необходимых параметров. Механизм сообщений реализуется с помощью вызова соответствующих функций. Таким образом, с помощью
ПРИМЕЧАНИЕ
Примером реализации событийно-управляемой модели может служить любая программа, управляемая с помощью меню. После запуска такая программа
Основными свойствами
Объединение данных с функциями их обработки в сочетании со скрытием ненужной для использования этих данных информации называется инкапсуляцией ( ).
Наследование - возможность создания
С++ каждый класс может иметь сколько угодно потомков и предков. Иногда предки называются
Третьим китом, на котором стоит
Понятие С++ весьма широко. Простым примером
Использование при программировании понятий, более близких к
Плохо спроектированная
Сейчас мы перейдем к рассмотрению фундамента, без которого невозможно написать ни одну объектно-ориентированную программу - синтаксических правил описания объектов, а потом вернемся к обсуждению принципов
Класс - это описание определяемого типа. Любой тип данных представляет собой множество значений и набор действий, которые разрешается выполнять с этими значениями. Например, сами по себе числа не представляют интереса - нужно иметь возможность ими оперировать: складывать, вычитать, вычислять квадратный корень и т. д. В С++ множество значений нового типа определяется задаваемой в классе С++.
Данные класса называются полями (по аналогии с полями структуры), а функции класса - методами. Поля и методы называются элементами класса. Описание класса в первом приближении выглядит так:
class <имя>{
[ private: ]
<описание скрытых элементов>
public:
<описание доступных элементов>
}; // Описание заканчивается точкой с запятой
Спецификаторы доступа private и public управляют видимостью элементов класса. Элементы, описанные после служебного слова private, видимы только внутри класса. Этот вид доступа принят в классе по умолчанию. Интерфейс класса описывается после public. Действие любого private и public, порядок их следования значения не имеет.
Поля класса:
const ), при этом они инициализируются только один раз (с помощью static , но не как auto , extern и register .Инициализация полей при описании не допускается.
Классы могут быть глобальными (объявленными вне любого блока) и локальными (объявленными внутри блока, например, внутри функции или внутри другого класса). Обычно классы определяются глобально.
Локальные классы имеют некоторые особенности:
static ) и внешние ( extern ) переменные, inline ); (уточнение: согласно современным стандартам это необязательно)В качестве примера создадим класс, моделирующий персонаж компьютерной игры. Для этого требуется задать его свойства (например, количество щупалец, силу или наличие гранатомета) и поведение. Естественно, пример будет схематичен, поскольку приводится лишь для демонстрации синтаксиса.
class monster
{
int health, ammo;
public:
monster(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;}};
В этом классе два и ammo, получить значения которых извне можно с помощью методов get_health() и get_ammo(). Доступ к полям с помощью методов в данном случае кажется искусственным усложнением, но надо учитывать, что полями реальных классов могут быть сложные динамические структуры, и получение значений их элементов не так тривиально. Кроме того, очень важной является возможность вносить в эти структуры изменения, не затрагивая интерфейс класса.
В приведенном классе содержится три определения методов и одно объявление (метод ). Если ). Как правило, встроенными делают короткие методы. Если внутри класса записано только объявление (
void monster::draw(int x, int y, int scale, int position)
{ /* тело метода */}
Встроенные методы можно определить и вне класса с помощью (как и для обычных функций, она носит рекомендательный характер):
inline int monster::get_ammo()
{return ammo;}
Методы можно перегружать (это одно из проявлений полиморфизма ), а также объявлять либо константными, либо статическими (но не одновременно).
В каждом классе есть метод, имя которого совпадает с именем класса. Он называется конструктором и вызывается автоматически при создании объекта класса.
Типы данных struct и являются специальными видами класса.
Конкретные переменные типа данных "класс" называются экземплярами класса, или объектами. Время жизни и видимость объектов зависит от вида и места описания и подчиняется общим правилам С++:
monster Vasia; // Объект класса monster с параметрами по умолчанию monster Super(200, 300);// Объект с явной инициализацией monster stado[100]; // Массив объектов с параметрами по умолчанию /* Динамический объект (второй параметр задается по умолчанию) */ monster *beavis = new monster (10); monster butthead = Vasia; // Ссылка на объект
При создании каждого объекта выделяется память, достаточная для хранения всех его полей, и автоматически вызывается
Доступ к открытым ( public ) элементам объекта аналогичен доступу к полям структуры. Для этого используются операция . (точка) при обращении к элементу через
объект.поле указатель -> поле (*указатель).поле объект.метод( параметры ) указатель -> метод( параметры ) (*указатель).метод( параметры )
Обращение к открытому полю и
имя_массива[ индекс ].поле имя_массива[ индекс ].метод( параметры )
Например:
int n = Vasia.get_ammo(); stado[5].draw; cout << beavis->get_health();
Получить или изменить значения private элементов можно только через обращение к соответствующим методам.
Можно создать константный объект,значения полей которого изменять запрещается. К нему должны применяться только константные методы:
class monster{
...
int get_health() const {return health;}
};
const monster Dead (0,0); // Константный объект
cout << Dead.get_health();
Константный метод:
const после списка параметров;Рекомендуется описывать как константные те методы, которые предназначены для получения значений полей.
Каждый объект содержит свой экземпляр
Каждый нестатический метод, помимо явно объявленных параметров, получает еще один скрытый параметр: константный указатель на объект, для которого он вызван. В С++ это указатель обозначается зарезервированным словом this. Когда имя параметра метода совпадает с именем this -> num).
Выражение *this представляет собой return *this; ).
Для иллюстрации использования указателя this добавим в приведенный выше класс monster новый метод, возвращающий ссылку на наиболее здорового (поле ) из двух монстров, один из которых вызывает метод, а другой передается ему в качестве параметра (метод нужно поместить в секцию public описания класса):
monster the_best(monster M)
{
if( health > M.get_health())
return *this;
return M;
}
...
monster Vasia(50), Super(200);
// Новый объект Best инициализируется значениями полей Super
monster Best = Vasia.the_best(Super);
void. Нельзя получить указатель на const , virtual и static ).main. Локальные объекты создаются, как только становится При объявлении объектов вызывается один из конструкторов. При отсутствии инициализирующего выражения в объявлении объекта вызывается
Примеры:
monster Super(200, 300), Vasia(50); monster X = monster(1000);
В последнем операторе создается объект Х, которому присваивается безымянный объект со значением параметра (значения остальных параметров устанавливаются по умолчанию).
При создании
В качестве примера класса с несколькими конструкторами усовершенствуем описанный ранее класс monster, добавив в него поля, задающие цвет ( ) и имя ( name ):
enum color {red, green, blue}; // Возможные значения цвета
class monster
{
int health, ammo;
color skin;
char *name;
public:
monster(int he = 100, int am = 10);
monster(color sk);
monster(char * nam);
...
};
//--------------------------------
monster::monster(int he, int am)
{ health = he; ammo = am; skin = red; name = 0;}
//--------------------------------
monster::monster(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;
}
}
//--------------------------------
monster::monster(char * nam)
{
/* К длине строки добавляется 1 для хранения нуль-символа */
name = new char [strlen(nam) + 1];
strcpy(name, nam);
health = 100; ammo = 10; skin = red;
}
//--------------------------------
monster * m = new monster ("Ork");
monster Green (green);
Первый из приведенных выше конструкторов является конструктором по умолчанию, поскольку его можно вызвать без параметров. Объекты класса monster теперь можно инициализировать различными способами, требуемый
Существует еще один способ инициализации полей в конструкторе (кроме уже описанного
monster::monster(int he, int am):
health (he), ammo (am), skin (red), name (0){}
Поля перечисляются через запятую. Для каждого поля в скобках указывается инициализирующее значение, которое может быть выражением. Без этого способа не обойтись при инициализации полей-констант, полей-ссылок и полей-объектов. В последнем случае будет вызван
T::T(const T) { ... /* Тело конструктора */ }
где T -
Если программист не указал ни одного
Запишем monster. Поскольку в нем есть поле name, содержащее указатель на
monster::monster(const monster M)
{
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;
}
...
monster Vasia (blue);
monster Super = Vasia; // Работает конструктор копирования
monster *m = new monster ("Ork");
monster Green = *m; // Работает конструктор копирования
С помощью модификатора можно описать
Память под статическое поле выделяется один раз при его инициализации независимо от числа созданных объектов (и даже при их отсутствии) и инициализируется с помощью операции доступа к
class A
{
public:
static int count;
}
...
A::count = 0;
/* будет выведено одно и то же */ A *a, b; * cout << A::count << a->count << b.count;
На private, нельзя инициализировать с помощью операции доступа к
Память, занимаемая статическим полем, не учитывается при определении размера объекта операцией sizeof.
Классическое применение статических полей - подсчет объектов. Для этого в классе объявляется целочисленное поле, которое увеличивается в
this. Обращение к статическим методам производится так же, как к статическим полям - либо через
) и виртуальными ( ).
Иногда желательно иметь непосредственный доступ извне к
Дружественная функция объявляется внутри класса, к элементам которого ей нужен доступ, с ключевым словом . В качестве параметра ей должен передаваться объект или ссылка на объект класса, поскольку указатель this ей не передается. Одна функция может "дружить" сразу с несколькими классами.
Дружественная функция может быть обычной функцией или методом другого ранее определенного класса. На нее не распространяется действие
В качестве примера ниже приведено описание двух функций, дружественных классу monster. Функция является hero, а функция steal_ammo не принадлежит ни одному классу. Обеим функциям в качестве параметра передается ссылка на объект класса monster.
class monster; // Предварительное объявление класса
class hero
{
...
void kill(monster );
};
class monster
{
...
friend int steal_ammo(monster );
/* Класс hero должен быть определен ранее */
friend void hero::kill(monster );
};
int steal_ammo(monster M){return --M.ammo;}
void hero::kill(monster M){M.health = 0; M.ammo = 0;}
Если все методы какого-либо класса должны иметь доступ к . В приведенном ниже примере класс mistress объявляется дружественным классу hero:
class hero{
... friend class mistress;}
class mistress{
... void f1();
void f2();}
Функции f1 и f2 являются дружественными по отношению к классу hero (хотя и описаны без ключевого слова ) и имеют доступ ко всем его полям.
Объявление не является
main ;delete (автоматический вызов деструктора при выходе указателя из При уничтожении массива delete. При выполнении операции delete[]
Имя деструктора начинается с
const или static ;new выделялся конкретный адрес.Если
Описывать в классе
monster::~monster() {delete [] name;}
С++ позволяет переопределить действие большинства операций так, чтобы при использовании с объектами конкретного класса они выполняли заданные функции. Эта дает возможность использовать собственные типы данных точно так же, как стандартные. Обозначения собственных операций вводить нельзя. Можно перегружать любые операции, существующие в С++, за исключением:
| . | .* | ?: | :: | # | ## | sizeof |
Функцию-операцию можно определить тремя способами: она должна быть либо
Функция-операция содержит ключевое слово , за которым следует знак переопределяемой операции:
тип operator операция ( список параметров) { тело функции }
Унарная функция-операция, определяемая внутри класса, должна быть представлена с помощью нестатического метода без параметров, при этом
class monster
{... monster operator ++() {++health; return *this;}}
monster Vasia;
cout << (++Vasia).get_health();
Если функция определяется вне класса, она должна иметь один параметр типа класса:
class monster
{... friend monster operator ++( monster M);};
monster operator ++(monster M) {++M.health; return M;}
Если не описывать функцию внутри класса как дружественную, нужно учитывать доступность изменяемых полей (в данном случае поле недоступно извне, так как описано со private, поэтому для его изменения требуется использование соответствующего метода, не описанного в приведенном примере).
Операции постфиксного . Он используется только для того, чтобы отличить их от префиксной формы:
class monster
{... monster operator ++(int){monster M(*this); health++; return M;}};
monster Vasia;
cout << (Vasia++).get_health();
Бинарная функция-операция, определяемая внутри класса, должна быть представлена с помощью нестатического метода с параметрами, при этом вызвавший ее объект считается первым
class monster
{ ...
bool operator >( const monster M)
{
if( health > M.get_health()) return true;
return false;
}
};
Если функция определяется вне класса, она должна иметь два параметра типа класса:
bool operator >(const monster M1, const monster M2)
{ if( M1.get_health() > M2.get_health()) return true;
return false;
}
monster operator = (const monster M)
{
if (M == this) return *this; // Проверка на самоприсваивание
if (name) delete [] name;
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;
return *this;
}
Операцию
Можно определить функции-операции, которые будут осуществлять преобразование класса к другому типу. Формат:
operator имя_нового_типа ();
Тип
Пример:
monster::operator int(){return health;}
...
monster Vasia; cout << int(Vasia);
К элементам классов можно обращаться с помощью указателей. Для этого определены операции .* и ->*. Указатели на поля и
Формат указателя на метод класса:
возвр_тип (имя_класса::*имя_указателя)(параметры);
Например, описание указателя на monster
int get_health() const {return health;}
будет иметь вид:
int (monster:: *pget)();
Такой указатель можно задавать в качестве
void fun(int (monster:: *pget)())
{
(*this.*pget)(); // Вызов функции через операцию .*
(this->*pget)(); // Вызов функции через операцию ->*
}
Можно настроить указатель на конкретный метод с помощью операции взятия адреса:
pget = monster::get_health;// Присваивание значения указателю monster Vasia, *p; p = new monster; int Vasin_health = (Vasia.*pget)();//Вызов функции через операцию .* int p_health = (p->*pget)(); // Вызов функции через операцию ->*
Как и указатели на обычные функции, указатели на методы используются в том случае, когда возникает необходимость вызвать метод, имя которого неизвестно. Однако в отличие указателя на переменную или обычную функцию, указатель на метод не ссылается на определенный адрес памяти. Он больше похож на индекс в массиве, поскольку задает смещение. Конкретный адрес в памяти получается путем сочетания указателя на метод с указателем на определенный объект.
ПРИМЕЧАНИЕ
Методы, вызываемые через указатели, могут быть виртуальными. При этом вызывается метод, соответствующий
Формат указателя на поле класса:
тип_данных(имя_класса::*имя_указателя);
В определение указателя можно включить его инициализацию в форме:
имя_класса::*имя_поля; // Поле должно быть public
Если бы поле было объявлено как public, определение указателя на него имело бы вид:
int (monster::*phealth) = monster::health; cout << Vasia.*phealth; // Обращение через операцию .* cout << p->*phealth; // Обращение через операцию ->*
Обратите внимание на то, что указатели на поля классов не являются обычными указателями - ведь при присваивании им значений они не ссылаются на конкретный адрес памяти, поскольку память выделяется не под классы, а под объекты классов.
Как правило, класс как тип, определенный пользователем, содержит скрытые поля и следующие функции:
const , указывающим, что они не должны изменять значения полей);Если существуют функции, которые работают с классом или несколькими классами через интерфейс (то есть доступ к
namespace Staff{
class monster { /* * */};
class hero { /* * */};
void interact(hero, monster); ... }
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.