|
Структуры и классы
Структуры
Структура является набором элементов произвольного типа (кроме типа void). Таким образом, структура объединяет логически связанные данные разных типов.
Объявление структуры
Структурный тип данных определяется следующим описанием:
struct имя_структуры |
Имя структуры нельзя использовать в качестве идентификатора (имени переменной или функции). Следует иметь ввиду, что в MQL5 элементы структуры следуют непосредственно друг за другом без выравнивания. В языке C++ такое указание делается компилятору с помощью инструкции
#pragma pack(1) |
Если требуется сделать иное выравнивание в структуре, необходимо использовать вспомогательные члены-"заполнители" нужных размеров.
Пример:
struct trade_settings |
Такое описание выровненных структур необходимо только для передачи в импортированные dll-функции.
Внимание: данный пример иллюстрирует неправильно спроектированные данные. Лучше было бы сначала объявить данные take и stop большего размера типа double, а затем объявить член slippage типа uchar. В этом случае внутреннее представление данных будет всегда одинаково независимо от значения, указанного в #pragma pack().
Если структура содержит переменные типа string и/или объект динамического массива, то компилятор назначает для такой структуры неявный конструктор, в котором производится обнуление всех членов структуры типа string и правильная инициализация для объекта динамического массива.
Простые структуры
Структуры, которые не содержат строки, объекты класса и объекты динамических массивов, называются простыми структурами; переменные таких структур могут свободно копироваться друг в друга, даже если это разные структуры. Переменные простых структур, а также их массивы можно передавать в качестве параметров в импортируемые из DLL функции.
Доступ к членам структуры
Имя структуры является новым типом данных и позволяет объявлять переменные этого типа. Структура может быть объявлена только один раз в пределах проекта. Доступ к членам структур производится при помощи операции точка (.).
Пример:
struct trade_settings |
Классы имеют ряд отличий от структур:
·в объявлении используется ключевое слово class;
·по умолчанию все члены класса имеют спецификатор доступа private, если не указано иное. Члены-данные структуры по умолчанию имеют тип доступа public, если не указано иное;
·объекты классов всегда имеют таблицу виртуальных функций, даже если в классе не объявлено ни одной виртуальной функции. Структуры не могут иметь виртуальных функций;
·к объектам класса можно применять оператор new, к структурам этот оператор применять нельзя;
·классы могут наследоваться только от классов, структуры могут наследоваться только от структур.
Классы и структуры могут иметь явный конструктор и деструктор. В случае если явно определен конструктор, инициализация переменной типа структуры или класса при помощи инициализирующей последовательности невозможна.
Пример:
struct trade_settings |
Конструкторы и деструкторы
Конструктор - это специальная функция, которая вызывается автоматически при создании объекта структуры или класса и обычно используется для инициализации членов класса. Далее мы будем говорить только о классах, при этом все сказанное относится и к структурам, если не оговорено иное. Имя конструктора должно совпадать с именем класса. Конструктор не имеет возвращаемого типа (можно указать тип void).
Определенные члены класса – строки, динамические массивы и объекты, требующие инициализации – в любом случае будут проинициализированы, независимо от наличия конструктора.
Каждый класс может иметь несколько конструкторов, отличающихся по количеству параметров и спискам инициализации. Конструктор, требующий указания параметров, называется параметрическим конструктором.
Конструктор, не имеющий параметров, называется конструктором по умолчанию. Если в классе не объявлен ни один конструктор, то компилятор сам создаст конструктор по умолчанию при компиляции.
//+------------------------------------------------------------------+ |
Конструктор можно объявить в описании класса, а затем определить его тело. Например, вот так могут быть определены два конструктора класса MyDateClass:
//+------------------------------------------------------------------+ |
В конструкторе по умолчанию заполняются все члены класса с помощью функции TimeCurrent(), в конструкторе с параметрами заполняются только значения часа. Остальные члены класса (m_year, m_month и m_day) будут проинициализированы автоматически текущей датой.
Конструктор по умолчанию имеет специальное назначение при инициализации массива объектов своего класса. Конструктор, все параметры которого имеют значения по умолчанию, не является конструктором по умолчанию. Покажем это на примере:
//+------------------------------------------------------------------+ |
Если раскомментировать в этом примере строки
//CFoo foo_array[3]; // такой вариант использовать нельзя - конструктор по умолчанию не задан |
или
//CFoo foo_dyn_array[]; // такой вариант использовать нельзя - конструктор по умолчанию не задан |
то компилятор выдаст на них ошибку "default constructor is not defined".
Если класс имеет конструктор, объявленный пользователем, то конструктор по умолчанию не будет сгенерирован компилятором. Это означает, что если в классе объявлен конструктор с параметрами, но не объявлен конструктор по умолчанию, то нельзя объявлять массивы объектов этого класса. Вот на таком скрипте компилятор сообщит об ошибке:
//+------------------------------------------------------------------+ |
В данном примере класс CFoo имеет объявленный параметрический конструктор – в таких случаях компилятор при компиляции не создает автоматически конструктор по умолчанию. В то же время при объявлении массива объектов предполагается, что все объекты должны быть созданы и инициализированы автоматически. При автоматической инициализации объекта требуется вызвать конструктор по умолчанию, но так как конструктор по умолчанию явно не объявлен и не сгенерирован автоматически компилятором, то создание такого объекта невозможно. Именно по этой причине компилятор выдает ошибку еще на этапе компиляции.
Существует специальный синтаксис для инициализации объекта с помощью конструктора. Инициализаторы конструктора (специальные конструкции для инициализации) для членов структуры или класса могут быть заданы в списке инициализации.
Список инициализации – это список инициализаторов, разделенных запятыми, который идет после двоеточия за списком параметров конструктора и предшествует телу (идет перед открывающей фигурной скобкой). Есть несколько требований:
·списки инициализации можно использовать только в конструкторах;
·в списке инициализации нельзя инициализировать члены родителей;
·после списка инициализации должно идти определение (реализация) функции.
Покажем пример нескольких конструкторов для инициализации членов класса.
//+------------------------------------------------------------------+ |
В данном случае класс CPerson имеет три конструктора:
1.явный конструктор по умолчанию, который позволяет создавать массив объектов данного класса;
2.конструктор с одним параметром, который принимает в качестве параметра полное имя и разделяет его на имя и фамилию по найденному пробелу;
3.конструктор с двумя параметрами, который содержит список инициализации. Инициализаторы – m_second_name(surname) и m_first_name(name).
Обратите внимание, как инициализация с помощью списка заменила присваивание. Отдельные члены должны быть инициализированы как:
член_класса (список выражений) |
В списке инициализации члены могут идти в любом порядке, но при этом все члены класса будут инициализироваться согласно порядку их объявления. Это означает, что в третьем конструкторе сначала будет инициализирован член m_first_name, так как он объявлен первым, и только после него будет инициализирован член m_second_name. Это необходимо учитывать в тех случаях, когда инициализация одних членов класса зависит от значений в других членах класса.
Если в базовом классе не объявлен конструктор по умолчанию и при этом объявлен один или несколько конструкторов с параметрами, то нужно обязательно вызвать один из конструкторов базового класса в списке инициализации. Он идет через запятую как обычные члены списка и будет вызван в первую очередь при инициализации объекта независимо от местоположения в списке инициализации.
//+------------------------------------------------------------------+ |
В приведенном примере при создании объекта bar будет вызван конструктор по умолчанию CBar(), в котором сначала вызывается конструктор для предка CFoo, а затем конструктор для члена класса m_member.
Деструктор - это специальная функция, которая вызывается автоматически при уничтожении объекта класса. Имя деструктора записывается как имя класса с тильдой (~). Строки, динамические массивы и объекты, требующие деинициализации, в любом случае будут деинициализированы независимо от наличия деструктора. При наличии деструктора, эти действия будут произведены после вызова деструктора.
Деструкторы всегда являются виртуальными, независимо от того, объявлены они с ключевым слово virtual или нет.
Определение методов классов
Функции-методы класса могут быть определены как внутри класса, так и за пределами объявления класса. Если метод определяется внутри класса, то его тело следует непосредственно после объявления метода.
Пример:
class CTetrisShape |
Функции с SetRightBorder(int border) по Draw() объявлены и определены прямо внутри класса CTetrisShape.
Конструктор CTetrisShape() и методы CheckDown(int& pad_array[]), CheckLeft(int& side_row[]) и CheckRight(int& side_row[]) только объявлены внутри класса, но пока не определены. Определения этих функций должны следовать далее по коду. Для того чтобы определить метод вне класса используется операция разрешения контекста, в качестве контекста используется имя класса.
Пример:
//+------------------------------------------------------------------+ |
Модификаторы доступа public, protected и private
При разработке нового класса рекомендуется ограничивать доступ к членам извне. Для этих целей используются ключевые слова private или protected. Доступ в этом случае к сокрытым данным может осуществляться только из функций-методов этого же класса. Если использовано ключевое слово protected, то доступ к сокрытым данным может осуществляться и из методов классов - наследников этого класса. Точно таким же образом может ограничиваться доступ и к функциям-методам класса.
Если необходимо полностью открыть доступ к членам и/или методам класса, то используется ключевое слово public.
Пример:
class CTetrisField |
Любые члены и методы класса, объявленные после спецификатора public: (и до следующего спецификатора доступа), доступны при любом обращении программы к объекту этого класса. В данном примере это следующие члены: функции CTetrisField(), Init(), Deinit(), Down(), Left(), Right(), Rotate() и Drop().
Любые члены класса, объявленные после спецификатора доступа к элементам private: (и до следующего спецификатора доступа), доступны только функциям-членам этого класса. Спецификаторы доступа к элементам всегда заканчиваются двоеточием (:) и могут появляться в определении класса много раз.
Доступ к членам базового класса может переопределяться при наследовании в производных классах.
Смотри также
Объектно-ориентированное программирование