Главная страница
Навигация по странице:

  • 3.3 Глобальные и локальные классы

  • 3.4 Примеры классов

  • 3.5 Inline функции

  • 3.6 Указатель this

  • 4.2 Конструкторы по умолчанию

  • 4.3 Конструктор копирования

  • 4.4 Статические элементы класса С помощью модификатора static можно описать статические поля и методы класса. 4.4.1 Статические поля

  • 4.4.2 Статические методы

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


    Скачать 1.73 Mb.
    НазваниеОбъектноориентированное программирование
    Анкорттттт
    Дата30.10.2021
    Размер1.73 Mb.
    Формат файлаpdf
    Имя файлаOOP-PrePrint.pdf
    ТипКонспект
    #259341
    страница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
    . Статические поля нельзя инициа- лизировать в конструкторе, так как они создаются до создания любого объ- екта.
    Классическое применение статических полей – подсчет объектов. Для этого в классе объявляется целочисленное поле, которое увеличивается в конструкторе и уменьшается в деструкторе.
    4.4.2 Статические методы
    Статические методы могут обращаться непосредственно только к стати- ческим полям и вызывать только другие статические методы класса, по- скольку им не передается скрытый указатель this
    . Обращение к статиче- ским методам производится так же, как к статическим полям – либо через имя класса, либо, если хотя бы один объект класса уже создан, через имя объекта.
    Статические методы не могут быть константными (
    const
    ) и виртуаль- ными (
    virtual
    ).
    1   2   3   4   5   6   7   8   9   ...   15


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