
- •6Vpj7-h3cxh-hbtpt-x4t74-3yvy7
- •Оглавление
- •Предисловие
- •Введение
- •1.1. Понятие класса и объекта. Инкапсуляция
- •1.2. Определение классов. Компоненты. Доступность
- •Class_key /*class_id*/ { /*members_list*/ };
- •Value_type class_id::function_id(parameters) {statements}
- •CPoint point1(100,70); // локальный объект
- •Static cPoint point3(50,120); // статический объект
- •Class_id(parameters) /*:initializer_list*/ {/*statements*/}
- •CString(const char *);
- •Delete[] __thematrix;
- •1.4. Обращение к компонентам объектов
- •1.5. Статические и нестатические компоненты классов
- •1.7. Указатель this
- •В опросы для самопроверки
- •2. Механизм наследования. Полиморфизм
- •2.1. Формы наследования. Базовые и производные классы
- •Class_key class_id: inheritance_specifier base_class_id {member_list};
- •2.3. Абстрактные классы
- •2.4. Множественное наследование и виртуальные классы
- •2.5. Преобразование динамических типов. Динамическая идентификация типов
- •Catch ( std::bad_cast & ) { // обработка исключения
- •Return 0;
- •Вопросы для самопроверки
- •3. Дружественные функции и классы
- •3.1. Дружественные функции
- •3.2. Дружественные классы
- •Вопросы для самопроверки
- •4. Механизм вложения
- •4.1. Вложенные классы
- •4.2. Локальные классы
- •Вопросы для самопроверки
- •5. Объектная модель и шаблоны
- •5.1. Определение, описание и инстанцирование шаблонов
- •::Function_id(function_parameter_list) { statements }
- •5.2. Параметры и аргументы шаблонов
- •Class identifier typename identifier
- •// Key, Data – параметры-типы (типы ключа и данных отображения)
- •// Container – контейнер, где содержится информация отображения class сMap {
- •Class MyTemplate
- •Int array[10]; struct Structure { int m; static int sm; } str;
- •5.3. Шаблоны компонентных функций
- •Value_type function_template_id(function_parameter_list) { statements }
- •::Function_template_id(function_parameter_list) { statements }
- •5.4. Специализация шаблонов
- •Вопросы для самопроверки
- •6. Перегрузка операций
- •Value_type operator @ (parameter_list);
- •Value_type operator @ (parameter_list) { statements }
- •Return fail();
- •6.3. Перегрузка бинарных операций
- •Value_type operator @ (parameter); // компонентная функция
- •Value_type operator @ (parameter, parameter); // глобальная функция friend value_type operator @ (parameter, parameter); // дружественная функция
- •Return *this;
- •Return *this;
- •/* Присваиваем собственные данные класса d */
- •6.4. Перегрузка операций управления памятью
- •Typedef void (*new_handler) ();
- •Extern new_handler set_new_handler( new_handler new_p );
- •Void operator delete(void * memory) {
- •... // Специальная обработка пользователя ::operator delete(memory); // освободить память
- •Вопросы для самопроверки
- •7. Механизм исключений
- •Throw expression
- •7.3. Специальные средства поддержки механизма исключений
- •Unexpected_function set_unexpected(unexpected_function func_name);
- •Typedef void (* unexpected_function) ();
- •Extern char * __throwExceptionName; extern char * __throwFileName; extern unsigned __throwLineNumber;
- •Вопросы для самопроверки
- •8. Подсчет ссылок
- •8.1. Назначение механизма подсчета ссылок
- •8.2. Контекстно-независимая модель счетчика ссылок
- •8.4. Внедрение подсчета ссылок в существующий класс
- •Вопросы для самопроверки
- •9. Стандартная библиотека шаблонов (stl)
- •9.1. Назначение и архитектура stl
- •9.2. Последовательные контейнеры
- •Class vector {
- •// Определение итераторов
- •Sort(first,last); // сортировка вектора в диапазоне итераторов
- •Ifstream ifile ("example.In"); ofstream ofile ("example.Out");
- •OutputIterator copy(
- •InputIterator first, InputIterator last, OutputIterator result );
- •// Заполнение списка
- •Operator- (int)
- •Operator- (random access iterator) operator[] (int)
- •InputIterator find(InputIterator first, InputIterator last, const t & value);
- •InputIterator find(InputIterator first, InputIterator last, const t & value)
- •Return first;
- •OutputIterator copy (InputIterator first, InputIterator last, OutputIterator result)
- •Return result;
- •OutputIterator transform (InputIterator first, InputIterator last, OutputIterator result, UnaryOperation op)
- •Return result;
- •Void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp)
- •__Quick_sort_loop(first, last, comp); __final_insertion_sort(first, last, comp);
- •T accumulate(InputIterator first, InputIterator last, t init, Function f);
- •V.Push_back(2); V.Push_back(5);
- •9.5. Функторы
- •T operator()(const t & X) const { return -X; }
- •9.7. Адаптеры
- •S1.Push(1); s1.Push(5);
- •// Записать в вектор числа 1 2 3 4
- •// Сортировать по неубыванию
- •// Записать в вектор числа 4 6 10 3 13 2
- •Вопросы для самопроверки
- •Заключение
- •Библиографический Список
- •6Vpj7-h3cxh-hbtpt-x4t74-3yvy7
1.5. Статические и нестатические компоненты классов
Каждый объект содержит экземпляр каждого компонента, который объявлен в теле класса. Значения этих компонент для разных объектов в общем случае различны и определяют текущее состояние объекта. Компонентные функции класса существуют в одном экземпляре, независимо от числа объектов класса в программе, и вызываются для обработки данных конкретного объекта. В то же время в классе можно определить компонентные данные, которые будут общими сразу для всех объектов класса. Такие данные могут быть полезны, например, для моделирования общих свойств объектов. Так, для класса CPoint, определенного выше, можно ввести компоненты, отражающие максимальные координаты точек по оси X и по оси Y. Очевидно, нет смысла хранить значения этих компонент в каждом объекте-точке, поскольку их значения для всех точек одинаковы.
Общие для всех объектов компонентные данные принято называть статическими. Они существуют в единственном экземпляре, причем размещаются в специально отведенной области памяти статических переменных. Обычные же (нестатические) данные объектов размещаются в стеке или динамической памяти в зависимости от способа формирования объекта. Статический компонент создается до формирования объектов в момент его инициализации и существует после уничтожения всех объектов (продолжительность его существования определяется рамками модуля программы).
Чтобы сделать некоторый компонент класса статическим, нужно в теле класса перед его объявлением записать служебное слово static. При реализации классов все их статические компоненты должны быть определены. Определение осуществляется в том же пространстве имен, где определен класс (возможно, в отдельном cpp-файле), и имеет следующий формат:
value_type class_id::member_id initializer,
где value_type, class_id, member_id и initializer – тип статического компонента, имя класса, имя компонента и инициализатор соответственно.
Ниже дано модифицированное определение класса CPoint, куда включены статические компоненты. Показано, каким образом эти компоненты определяются.
// это пишем в файле cpoint.h
class CPoint {
public:
CPoint(int x, int x);
... /* здесь объявлены другие функции, см.выше */
static const int max_x; // предельные координаты точек
static const int max_y;
private:
int __x, __y; // текущие координаты
bool __visible; // признак видимости точки
};
...
// это пишем в файле cpoint.cpp
const int CPoint::max_x = 1024; // внешние определения компонент
const int CPoint::max_y = 768;
Инициализация статических компонент целых и перечислимых типов разрешается непосредственно в теле класса. Однако для них все равно необходимо внешнее определение. Если статический компонент не определяется, то возникает ошибка компоновки (unresolved external).
Статический компонент можно объявлять не во всяком классе; в частности, в локальных классах, безымянных классах и классах, вложенных в них, не может быть статических компонент.
Доступ к статическим компонентам возможен только после их инициализации и должен явно или неявно включать имя класса, а не объекта, к которому относится компонент. Формат обращения может быть следующим:
class_id::static_member_id
object_id.class_id::static_member_id
object_id.static_member_id
Например:
if ( x > CPoint::max_x || y > CPoint::max_y )
throw std::exception(“Invalid coordinates specified”);
Доступ к статическим компонентам открыт только в том случае, когда они записаны в секции public. Прямое обращение к статическим компонентам, равно как и к нестатическим, снижает уровень инкапсуляции класса и, следовательно, лучше обращаться к ним через соответствующие компонентные функции.
Статическим компонентам свойственна одна важная особенность: к ним можно обращаться сразу после инициализации даже при отсутствии объектов. Но вызов обычных компонентных функций возможен только через уже существующий объект, и поэтому работа со статическими компонентами через такие функции становится невозможной. Для преодоления этой проблемы в С++ введено понятие статических компонентных функций. Эти функции позволяют работать со статическими компонентами при отсутствии объектов.
Определяется статическая компонентная функция аналогично обычной, но с добавлением перед ее прототипом служебного слова static. Внешнее ее определение ничем не отличается от определения нестатической компонентной функции. Однако вызов такой функции возможен только с указанием имени содержащего ее класса (если имя класса не указать, то возникает ошибка компиляции). Безусловно, вызвать статическую функцию можно и через объект, и через указатель на него, и через ссылку (если объект уже сформирован). При этом по объекту неявно выводится класс, через который и вызывается статическая функция.
Ниже class CPoint переписан с использованием статических компонентных функций:
// это пишем в файле cpoint.h
class CPoint {
public:
CPoint(int x, int x);
... /* здесь объявлены другие функции, см.выше */
static int GetMaxX() { return max_x; } // статические функции
static int GetMaxY() { return max_y; }
private:
int __x, __y; // текущие координаты
bool __visible; // признак видимости точки
static const int max_x; // предельные координаты точек
static const int max_y;
};
...
// это пишем в файле cpoint.cpp
const int CPoint::max_x = 1024; // внешние определения компонент
const int CPoint::max_y = 768;
...
// а это уже в функции, использующей наш класс
if ( x > CPoint::GetMaxX() || y > CPoint::GetMaxY() )
throw std::exception(“Invalid coordinates specified”);
1.6. Функции, сохраняющие и не сохраняющие константность объекта
Компонентные функции обычно работают с компонентными данными (реже с другими компонентными функциями) объектов; они могут модифицировать данные объекта, меняя тем самым его текущее состояние. Часто функции не меняют данных объекта, а просто читают их значения (среди таких функций выделяются, например, функции, возвращающие атрибуты объекта). Функцию, которая по смыслу не должна менять состояние объекта, целесообразно сделать константной5. Это позволит устранить случайные попытки изменить данные объектов и тем самым повысит надежность программы. Для определения константной функции после ее заголовка необходимо добавить модификатор const. Данный модификатор сообщит компилятору о том, что все изменения данных объекта функцией следует квалифицировать как ошибки. В реализации константных функций также необходимо указывать модификатор const.
В качестве примера снова рассмотрим класс точек CPoint. В нем можно легко указать функции, которые не должны по смыслу менять данные объекта. Это функции GetX, GetY, IsVisible. Их целесообразно сделать константными. Остальные функции меняют один или более компонент и константными быть не могут.
Ниже приведено определение модифицированного класса CPoint с константными функциями (введенные ранее в этот класс дополнительные данные и функции здесь для упрощения опускаем).
// файл cpoint.h
class CPoint {
public:
CPoint(int x, int y);
int GetX() const;
int GetY() const;
bool IsVisible() const;
...
private:
int __x, _y;
bool __visible;
};
// файл cpoint.cpp
inline int CPoint::GetX() const { return __x; }
inline int CPoint::GetY() const { return __y; }
inline bool CPoint::IsVisible() const { return __visible; }
...
Наряду с модификатором const, нестатическая компонентная функция класса может получить модификатор volatile или const volatile. Наличие модификатора volatile сообщает о том, что состояние объекта при выполнении функции может измениться, причем как из-за явного воздействия этой функции, так и из-за влияния другой функции (которая может, например, выполняться в другом командном потоке). Модификатор const volatile сообщает о том, что вызываемая функция не может изменить состояние объекта, однако это может сделать другая функция.
При разработке класса может возникнуть ситуация, когда компонентная функция по смыслу не должна менять большую часть компонент объекта (но не все). Пусть, для примера, есть класс CReportPrintManager, который управляет отправкой отчета на печать. Предположим, что в нем есть компонент-флаг bool __needprint, значение которого определяет, нужно ли печатать отчет или нет (необходимость печати отчета может возникнуть, например, после его модификации). Допустим, отправкой «занимается» функция DoPrint. Как только отчет напечатан, функция DoPrint сбрасывает флаг __needprint. Сразу после изменения отчета флаг __needprint снова устанавливается. Ясно, что функция DoPrint по смыслу не должна менять состояние объекта CReportPrintManager, исключая флаг __needprint.
Чтобы, не нарушая константности функции DoPrint, обеспечить возможность модификации флага __needprint, следует объявить этот флаг «модифицируемым» с помощью служебного слова mutable. Данное слово указывается перед описанием компонента. mutable-компонент может изменять любая компонентная функция независимо от ее модификаторов. Слово mutable может добавляться только к нестатическим членам класса. Его также нельзя использовать для константных членов и ссылок.