Главная страница

Объектноориентированное программирование


Скачать 1.73 Mb.
НазваниеОбъектноориентированное программирование
Дата21.11.2018
Размер1.73 Mb.
Формат файлаpdf
Имя файлаOOP-PrePrint.pdf
ТипКонспект
#57177
страница3 из 15
1   2   3   4   5   6   7   8   9   ...   15
3.2
Спецификаторы public, private, protected
Спецификаторы доступа private и public управляют видимостью эле- ментов класса. Элементы, описанные после служебного слова private
, ви- димы только внутри класса. Этот вид доступа принят в классе по умолча- нию. Интерфейс класса описывается после спецификатора public
. Дей- ствие любого спецификатора распространяется до следующего специфика- тора или до конца класса. Можно задавать несколько секций private и public
, порядок их следования значения не имеет.
Поля класса:

26

могут быть простыми переменными любого типа, указателями, мас- сивами и ссылками (т.е. могут иметь практически любой тип, кроме типа этого же класса, но могут быть указателями или ссылками на этот класс);

могут быть константами (описаны с модификатором const
), при этом они инициализируются только один раз (с помощью конструктора) и не мо- гут изменяться;

могут быть описаны с модификатором static
, но не как auto
, extern и register
Инициализация полей при описании не допускается.
3.3
Глобальные и локальные классы
Классы могут быть глобальными (объявленными вне любого блока) и локальными (объявленными внутри блока, например, внутри функции или внутри другого класса). Обычно классы определяются глобально.
Локальные классы имеют некоторые особенности:

локальный класс не может иметь статических элементов;

внутри локального класса можно использовать из охватывающей его области типы, статические (
static
) и внешние (
extern
) переменные, внеш- ние функции и элементы перечислений;

запрещается использовать автоматические переменные из охватыва- ющей класс области;

методы локальных классов могут быть только встроенными (
inline
);

если один класс вложен в другой класс, они не имеют каких-либо осо- бых прав доступа к элементам друг друга и могут обращаться к ним только по общим правилам.
3.4
Примеры классов
В качестве примера создадим класс, моделирующий персонаж компью- терной игры. Для этого требуется задать его свойства (например, количе- ство щупалец, силу или наличие гранатомета) и поведение. Естественно, пример будет схематичен, поскольку приводится лишь для демонстрации синтаксиса. 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;}
};

27
В этом классе два скрытых поля – health и ammo
, получить значения которых извне можно с помощью методов get_health()
и get_ammo()
. До- ступ к полям с помощью методов в данном случае кажется искусственным усложнением, но надо учитывать, что полями реальных классов могут быть сложные динамические структуры, и получение значений их элементов не так тривиально. Кроме того, очень важной является возможность вносить в эти структуры изменения, не затрагивая интерфейс класса.
Методы класса имеют неограниченный непосредственный доступ к его полям. Внутри метода можно объявлять объекты, указатели и ссылки как своего, так и других классов.
В приведенном классе содержится три определения методов и одно объ- явление (метод draw
). Если тело метода определено внутри класса, он явля- ется встроенным (
inline
). Как правило, встроенными делают короткие ме- тоды. Если внутри класса записано только объявление (заголовок) метода, сам метод должен быть определен в другом месте программы с помощью операции доступа к области видимости: void monster::draw(int x, int y, int scale, int position)
{ /* тело метода */}
Встроенные методы можно определить и вне класса с помощью дирек- тивы inline
(как и для обычных функций, она носит рекомендательный ха- рактер): inline int monster::get_ammo()
{ return ammo; }
Методы можно перегружать (это одно из проявлений полиморфизма), а также объявлять либо константными, либо статическими (но не одновре- менно).
В каждом классе есть метод, имя которого совпадает с именем класса.
Он называется конструктором и вызывается автоматически при создании объекта класса. Конструктор предназначен для инициализации объекта. Ав- томатический вызов конструктора позволяет избежать ошибок, связанных с использованием неинициализированных переменных. Подробнее конструк- торы описываются далее в разделе «Конструкторы».
Типы данных struct и union являются специальными видами класса.
Конкретные переменные типа данных «класс» называются экземпля- рами класса, или объектами. Время жизни и видимость объектов зависит от вида и места описания и подчиняется общим правилам С++: monster Vasia;
// Объект класса monster с параметрами по умолчанию

28 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 после списка параметров;

не может изменять значения полей класса;

может вызывать только константные методы;

29

может вызываться для любых (не только константных) объектов.
Рекомендуется описывать как константные те методы, которые предна- значены для получения значений полей.
3.5
Inline функции
Встроенная функция – это функция, код которой прямо вставляется в том месте, где она вызвана. Как и макросы, определенные через
#define
, встроенные функции улучшают производительность за счет стоимости вы- зова и (особенно!) за счет возможности дополнительной оптимизации
(«процедурная интеграция»).
В обычном С вы можете получить «инкапсулированные структуры», по- мещая в них указатель на void
, и заставляя его указывать на настоящие дан- ные, тип которых неизвестен пользователям структуры. Таким образом, пользователи не знают, как интерпретировать эти данные, а функции до- ступа преобразуют указатель на void к нужному скрытому типу. Так дости- гается некоторый уровень инкапсуляции.
К сожалению, этот метод идет вразрез с безопасностью типов, а также требует вызова функции для доступа к любым полям структуры (если вы позволили бы прямой доступ, то его мог бы получить кто угодно, поскольку будет известно, как интерпретировать данные, на которые указывает void*
Такое поведение со стороны пользователя приведет к сложностям при по- следующем изменении структуры подлежащих данных).
Стоимость вызова функции невелика, но дает некоторую прибавку.
Классы С++ позволяют встраивание функций, что дает вам безопасность ин- капсуляции вместе со скоростью прямого доступа. Более того, типы пара- метры встраиваемых функций проверяются компилятором, что является преимуществом по сравнению с
#define макросами.
В отличие от
#define макросов, встроенные (
inline
) функции не под- вержены известным ошибкам двойного вычисления, поскольку каждый ар- гумент встроенной функции вычисляется только один раз. Другими сло- вами, вызов встроенной функции – это то же самое что и вызов обычной функции, только быстрее.
Также, в отличие от макросов, типы аргументов встроенных функций проверяются, и выполняются все необходимые преобразования.
3.6
Указатель this
Каждый объект содержит свой экземпляр полей класса. Методы места в классе не занимают и не дублируются для каждого объекта. Единственный

30 экземпляр метода используется всеми объектами совместно, поэтому каж- дый нестатический метод класса должен «знать», для какого объекта он вы- зван. Для этого, при вызове каждого нестатического метода класса, ему не- явно передается указатель на объект, вызвавший его
T * const this
Выражение
*this представляет собой разыменование указателя и имеет тип определяемого класса. Обычно это выражение возвращается в качестве результата, если метод возвращает ссылку на свой класс (
return *this;
).
Для иллюстрации использования указателя this добавим в приведен- ный выше класс monster новый метод, возвращающий ссылку на наиболее здорового (поле health
) из двух монстров, один из которых вызывает ме- тод, а другой передается ему в качестве параметра (метод нужно поместить в секцию 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);

31
4.
КОНСТРУКТОРЫ КЛАССОВ
4.1
Конструкторы и их свойства
Конструктор предназначен для инициализации объекта и вызывается ав- томатически при его создании. Ниже перечислены основные свойства кон- структоров.
1.
Конструктор не возвращает значения, даже типа void
. Нельзя полу- чить указатель на конструктор.
2.
Класс может иметь несколько конструкторов с разными параметрами для разных видов инициализации (при этом используется механизм пере- грузки).
3.
Конструктор, который можно вызвать без параметров, называется конструктором по умолчанию.
4.
Параметры конструктора могут иметь любой тип, кроме этого же класса. Можно задавать значения параметров по умолчанию. Их может со- держать только один из конструкторов.
5.
Если программист не указал ни одного конструктора, компилятор со- здает его автоматически (кроме случая, когда класс содержит константы и ссылки, поскольку их необходимо инициализировать). Такой конструктор вызывает конструкторы по умолчанию для полей класса и конструкторы ба- зовых классов.
6.
Конструкторы не наследуются.
7.
Конструктор не может быть константным, статическим и виртуаль- ным (нельзя использовать модификаторы const
, virtual и static
).
8.
Конструкторы глобальных объектов вызываются до вызова функции main
. Локальные объекты создаются, как только становится активной об- ласть их действия. Конструктор запускается и при создании временного объекта (например, при передаче объекта из функции).
При объявлении объектов вызывается один из конструкторов. При от- сутствии инициализирующего выражения в объявлении объекта вызывается конструктор по умолчанию, при инициализации другим объектом того же типа – конструктор копирования (см. далее), при инициализации полей – один из явно определенных конструкторов инициализации (т.е. конструкто- ров, которым передаются параметры для инициализации полей объекта).
4.2
Конструкторы по умолчанию
Конструкторы часто вызываются неявно для создания временных объек- тов. Обычно это происходит в следующих случаях:

32

при инициализации;

при выполнении операции присваивания;

для задания значений параметров по умолчанию;

при создании и инициализации массива;

при создании динамических объектов;

при передаче параметров в функцию и возврате результатов по значе- нию.
Примеры конструкторов: monster Super(200, 300), Vasia(50); monster X = monster(1000);
В последнем операторе создается объект
Х
, которому присваивается безымянный объект со значением параметра health = 1000
(значения остальных параметров устанавливаются по умолчанию).
При создании динамического массива вызывается конструктор без аргу- ментов.
В качестве примера класса с несколькими конструкторами усовершен- ствуем описанный ранее класс monster
, добавив в него поля, задающие цвет
(
skin
) и имя (
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];

33 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){}
Поля перечисляются через запятую. Для каждого поля в скобках указы- вается инициализирующее значение, которое может быть выражением. Без этого способа не обойтись при инициализации полей-констант, полей-ссы- лок и полей-объектов. В последнем случае будет вызван конструктор, соот- ветствующий указанным в скобках параметрам.
4.3
Конструктор копирования
Конструктор копирования – это специальный вид конструктора, получа- ющий в качестве единственного параметра указатель на объект этого же класса:
T::T(const T&) { ... /* Тело конструктора */ }
Здесь
T
– имя класса. Этот конструктор вызывается в тех случаях, когда новый объект создается путем копирования существующего:

при описании нового объекта с инициализацией другим объектом;

при передаче объекта в функцию по значению;

при возврате объекта из функции.
Если программист не указал ни одного конструктора копирования, ком- пилятор создает его автоматически. Такой конструктор выполняет поэле- ментное копирование полей. Если класс содержит указатели или ссылки,

34 это, скорее всего, будет неправильным, поскольку и копия, и оригинал бу- дут указывать на одну и ту же область памяти.
Запишем конструктор копирования для класса 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;
// Работает конструктор копирования
4.4
Статические элементы класса
С помощью модификатора static можно описать статические поля и методы класса.
4.4.1
Статические поля
Статические поля применяются для хранения данных, общих для всех объектов класса, например, количества объектов или ссылки на разделяе- мый всеми объектами ресурс. Эти поля существуют для всех объектов класса в единственном экземпляре, то есть не дублируются.
Память под статическое поле выделяется один раз при его инициализа- ции независимо от числа созданных объектов (и даже при их отсутствии) и инициализируется с помощью операции доступа к области действия, а не операции выбора: class A
{ public: static int count;
}
A::count = 0;
Статические поля доступны как через имя класса, так и через имя объ- екта:

35
/* будет выведено одно и то же */
A *a, b; * cout << A::count << a->count << b.count;
На статические поля распространяется действие спецификаторов до- ступа, поэтому статические поля, описанные как private
, нельзя инициали- зировать с помощью операции доступа к области действия, как описано выше. Им можно присвоить значения только с помощью статических мето- дов, как описано ниже.
Память, занимаемая статическим полем, не учитывается при определе- нии размера объекта операцией sizeof
. Статические поля нельзя инициа- лизировать в конструкторе, так как они создаются до создания любого объ- екта.
Классическое применение статических полей – подсчет объектов. Для этого в классе объявляется целочисленное поле, которое увеличивается в конструкторе и уменьшается в деструкторе.
1   2   3   4   5   6   7   8   9   ...   15


написать администратору сайта