Microsoft visual C++ и MFC© Александр Фролов, Григорий ФроловТом 24, М.: Диалог-МИФИ, 1993. Классы XE "классы"В программах, написанных на языке С, данные и функции, предназначенные для их обработки определяются отдельно. Такое разделение затрудняет структурированное программирование и создает дополнительные возможности для ошибок, которые трудно обнаружить. В С++ введено новое понятие - класс. Класс позволяет объединить данные и оперирующие ими функции в одной структуре. Такое объединение обычно называют инкапсуляцией данных и связанных с ними функций. Инкапсуляция позволяет скрыть конкретную реализацию класса, облегчая отладку и модификацию программ. Объявление класса имеет следующий вид: class [<tag>] { <member-list> } [<declarators>]; Когда вы определяете класс, то сначала указывается ключевое слово class, а затем в качестве аргумента <tag> имя самого класса. Это имя должно быть уникальным среди имен других классов, определенных в вашей программе. Затем в фигурных скобках следует список элементов класса <member-list>. В качестве элементов класса могут фигурировать данные (переменные), битовые поля, функции, вложенные классы, а также некоторые другие объекты. Вы можете включить качестве элемента класса указатель на другие объекты этого класса. Классы образуют собственное пространство имен. Имена элементов одного класса могут совпадать с именами элементов другого класса и даже с именами других переменных и функций определенных в программе. Функции входящие в класс, называются функциями-элементами, или следуя терминологии объектно-ориентированного подхода, методами. Далее мы будем называть такие функции методами. Внутри класса вы можете свободно обращаться ко всем его элементам функциям и данным, без указания имени класса или имени объекта этого класса. После закрывающей фигурной скобки в аргументе <declarators> можно объявить один или несколько объектов данного класса. Объекты класса можно объявить и позже, точно так же как объявляются переменные простых типов: [class] tag declarators; Ключевое слово class перед именем класса можно опустить. Для динамического создания и удаления объектов классов можно пользоваться операторами new и delete. Эти операторы позволяют легко строить списки классов, деревья и другие сложные структуры. Ключевое слово thisКлючевое слово this XE "this" представляет собой указатель на текущий объект класса. Методы класса могут использовать ключевое слово this чтобы получить указатель на объект для которого вызван данный метод. Указатель this представляет собой постоянную величину, вы не можете изменять его значение в своей программе. Разграничение доступа к элементам классаОпределив класс, вы можете создавать объекты этого класса и манипулировать ими, используя методы. Некоторые данные и методы, объединенные одним классом, можно сделать недоступными вне реализации класса, к другим можно будет обращаться из программы. Для управления доступом к элементам класса предусмотрены ключевые слова public, private и protect (спецификаторы доступа). Методы и данные, определенные или описанные после ключевого слова public представляют собой интерфейс класса - они доступны для использования вне определения класса. Остальные члены класса относятся к его внутренней реализации и обычно недоступны вне класса. Различия между членами класса, описанными после ключевых слов private и protect сказываются только при наследовании от данного класса новых классов. Процедуру наследования мы рассмотрим позже. Ключевые слова public XE "public" , private XE "private" и protect XE "protect" указываются в определении класса перед элементами класса, доступом к которым они управляют. Ключевые слова, управляющие доступом, могут быть указаны несколько раз в одном классе, порядок их расположения значения не имеет. По умолчанию элементы класса являются private. Рекомендуется всегда явно определять права доступа к членам класса. Ниже представлено определение класса Sample: class Sample { int iX; void Load(); public: void SetStr(); void GetStr(); char sDataText[80]; private: char sNameText[80]; int iIndex; public: void ConvertStr(); int iLevel; }; В классе описаны элементы данных iX, sDataText, sNameText, iIndex, iLevel и методы Load, SetStr, GetStr, ConvertStr. Элементы данных и методы SetStr, GetStr, sDataText, ConvertStr, iLevel объявлены public. К ним можно обращаться как из методов класса Sample, так и из программы. Остальные элементы класса объявлены как private. Доступ к ним открыт только для методов самого класса, а также дружественных функций и дружественных методов других классов. Дружественные функции и дружественные классы описаны в следующем разделе. Методы, входящие в классЕсли исходный текст метода XE "методы" очень короткий, то такой метод обычно определяется непосредственно внутри класса. Вы можете указать, что вместо вызова необходимо выполнять подстановку его тела. Для этого перед ее объявлением следует указать ключевое слово inline XE "inline" . Вот пример определения методов SetWeight и GetWeight непосредственно внутри класса: class line { public: void SetLength(int newLength) { length = newLength; } int GetLength() { return length; } private: int length; }; Если исходный код методов не такой короткий, то при определении класса указывается только объявление метода, а его определение размещается отдельно. Встраиваемые методы также можно определить вне класса. Когда вы определяете метод отдельно от класса, то имени метода должно предшествовать имя класса и оператор разрешения области видимости :: XE "оператор \:\:" . class convert { public: void GetString() { scanf(sText,"%s"); } void ShowString() { puts(sText); } int ConvertString(); void DummyString(); private: char sText[80]; }; void convert::ConvertString(void) { int i; for(i = 0; sText[i] != '\0'; i++ ) { sText[i] = tolower(sText[i]); } return i; } inline void convert::DummyString(void) { int i = 0; while(sText[i++]) sText[i] = 0; } Чтобы вызвать метод, надо сначала указать имя объекта класса, для которого будет вызван метод, а затем через точку имя метода. Вместо имени объекта можно использовать указатель на объект. В этом случае вместо символа точки надо использовать оператор -> XE "оператор ->" . Если метод вызывается из другого метода этого же класса, то имя объекта и оператор выбора элемента указывать не надо. Следующий пример демонстрирует вызов методов класса convert, исходный текст которого приведен выше: void main() { convert ObjectA; ObjectA.GetString(); ObjectA.ConvertString(); ObjectA.ShowString(); convert *pObjectB = new convert; pObjectB->GetString(); pObjectB->ConvertString(); pObjectB->ShowString(); } Методы класса могут быть перегружены. В одном и том же классе можно определить несколько методов с одинаковыми именами, но различным набором параметров. Конструкторы и деструкторы классаОбычно при создании объекта класса необходимо провести начальную инициализацию объекта, например выделить участок памяти для размещения каких-либо данных, связанных с этим объектом. После окончания использования объекта выделенную память надо освободить и отдать обратно операционной системе. Язык С++ предоставляет удобное средство для инициализации и удаления объектов класса. Для этого предусмотрены специальные методы. Они называются конструкторами XE "конструктор" и деструкторами XE "деструктор" . Функция конструктор имеет такое же имя как имя класса и позволяет выполнить инициализацию объекта класса в момент его создания. Конструктор может иметь параметры. Их надо будет указать при определении объекта данного класса. Класс может иметь несколько конструкторов с разными параметрами, то есть конструкторы могут быть перегружены. Класс BookList, представленный ниже, имеет два конструктора BookList. Первый конструктор не имеет параметров, второй конструктор имеет один параметр типа int: class BookList { // Конструкторы класса void BookList(); void BookList(int); // Остальные члены класса }; // Первый конструктор класса BookList::BookList(void) { } // Второй конструктор класса BookList::BookList(int iList) { } Когда вы создаете объекты класса, вы можете указать параметры для конструктора. Ниже создаются два объекта класса BookList - FirstBook и SecondBook: BookList FirstBook; BookList SecondBook(100); При создании первого объекта параметры конструктора не указываются, поэтому используется первый конструктор класса. А вот при создании второго объекта мы указали числовой параметр, поэтому в этом случае используется второй конструктор. Имя деструктора также соответствует имени класса, но перед ним должен стоять символ тильда. Деструктор вызывается автоматически, когда объект уничтожается. Например, если определить объект данного класса внутри блока, то при выходе из блока для объект будет вызвана функция деструктор. Функция деструктор не имеет параметров, поэтому она не может быть перегружена, а значит у данного класса может быть только один деструктор. Ниже представлен класс Object, для которого определен деструктор ~Object: class Object { void ~Object(); // Остальные члены класса }; Object::~Object(void) { } Методы, не изменяющие объекты классаЕсли метод не изменяет объект, для которого он вызывается, такой метод можно объявить с ключевым словом const XE "const" . Ключевое слово const указывается после закрывающей скобки списка аргументов метода. Вы должны указать, что метод не изменяет объект и в объявлении и в определении метода. Методы, объявленные как const XE "методы const" , не могут изменять элементы класса или вызывать другие методы, объявленные без ключевого слова const. Нарушение этих правил вызовет ошибку на этапе компиляции приложения. В библиотеке классов MFC вы встретите много методов, объявленных как const. Их использование повышает надежность приложения, так как компилятор сможет обнаружить ошибки, связанные с непреднамеренным изменением элементов класса. Ниже мы привели пример класса, для которого метод GetWeight определен как const. Если вы попытаетесь модифицировать элемент данных weight непосредственно из метода GetWeight, компилятор сообщит об ошибке. #include Статические методыВы можете объявить некоторые методы класса статическими методами. Для этого вы должны воспользоваться ключевым словом static. Статические методы не принимают параметр this XE "this" . На использование статических методов накладывается ряд ограничений. · Статические методы могут непосредственно обращаться только к статическим членам класса. · Статический метод не может быть объявлен как виртуальный метод. · Вы не можете определить нестатический метод с тем же именем и тем же набором параметров, что и статический метод класса. Статические методы имеют одну интересную особенность - вы можете вызывать их даже без создания объектов класса. Чтобы вызвать из программы статический метод, вы должны указать его полное имя, включая имя класса. Ниже представлен класс Circle, в котором определена статический метод GetPi. Он используется для получения значения статического элемента класса fPi. class Circle { public: static void GetPi() { return fPi; } private: static float fPi; }; float Circle::fPi = 3.1415; Вы можете вызвать метод GetPi следующим образом: class Circle { public: static void GetPi() { return fPi; } private: static float fPi; }; float Circle::fPi = 3.1415; Обратите внимание, что объект класса Circle не создается. Общие члены объектов классаИногда удобно, чтобы все объекты данного класса имели общие элементы данных, которые используются совместно. За счет этого можно существенно сократить количество глобальных переменных, улучшая структуру программы. Общие элементы данных класса следует объявить с ключевым словом static. Все общие элементы класса надо определить в тексте программы, зарезервировав за ними место в оперативной памяти: class CWindow { public: int xLeftTop, xRightBottom; int yLeftTop, yRightBottom; static char title[80]; void SetTitle(char*); }; char Cwindow::title[80] = "заголовок окна"; Каждый объект класса Cwindow будет иметь уникальные координаты, определяемые элементами данных xLeftTop, xRightBottom, yLeftTop, yRightBottom и одинаковый заголовок, хранимый элементом данных title. Общие элементы данных находятся в области действия своего класса. Методы класса могут обращаться к общим элементам точно так же, как к остальным данным из класса: void SetTitle(char* sSource) { strcpy(title, sSource); } Чтобы получить доступ к общим элементам из программы, надо объявить их как public XE "public" . Для обращения к такой переменной перед ее именем надо указать имя класса и оператор :: XE "оператор \:\:" . printf(Cwindow::title); Дружественные функции и дружественные классыДоступ к элементам класса из программы и других классов ограничен. Вы можете непосредственно обращаться только к элементам класса, определенным или описанным после ключевого слова public. Однако, в некоторых случаях, требуется определить функцию вне класса или другой класс, методы которого могут обращаться непосредственно ко всем элементам класса, включая элементы объявленные как private и protect. Дружественные функцииВ Си++ вы можете определить для класса так называемую дружественную функцию, воспользовавшись ключевым словом friend. В классе содержится только объявление дружественной функции. Ее определение расположено вне класса. Вы можете объявить дружественную функцию в любой секции класса - public, private или protect. Дружественная функция не является элементом класса, но может обращаться ко всем его элементам, включая private и protect. Одна и та же функция может быть дружественной для двух или более классов. В следующем примере определена функция Clear, дружественная для класса point. Дружественная функция Clear используется для изменения значения элементов данных m_x и m_y, объявленных как private: //========================================================== // Класс point class point { public: // Функция Clear объявляется дружественной классу point friend void point::Clear(point*); // Интерфейс класса... private: int m_x; int m_y; }; //========================================================== // Функция Clear void Clear(point* ptrPoint) { // Обращаемся к элементам класса, объявленным как private ptrPoint->m_x = 0; ptrPoint->m_y = 0; return; } //========================================================== // Главная функция void main() { point pointTestPoint; // Вызываем дружественную функцию Clear(&pointTestPoint); } С помощью ключевого слова friend вы можете объявить некоторые методы одного класса дружественными для другого класса. Такие методы могут обращаться ко всем элементам класса, даже объявленным как private и protect, несмотря на то, что сами они входят в другой класс. В следующем примере мы определяем два класса - line и point. В классе point определяем метод Set и объявляем его в классе line как дружественный. Дружественный метод Set может обращаться ко всем элементам класса line: // Предварительное объявление класса line class line; //========================================================== // Класс point class point { public: // Метод Set класса point void Set(line*); // ... }; //========================================================== // Класс line class line { public: // Метод Set класса point объявляется дружественной // классу point friend void point::Set(line*); private: int begin_x, begin_y; int end_x, end_y; }; //========================================================== // Функция Clear void point::Set(line* ptrLine) { // Обращаемся к элементам класса line, объявленным как // private ptrLine->begin_x = 0; ptrLine->begin_y = 0; // ... return; } //========================================================== // Главная функция void main() { point pointTestPoint; line lineTestPoint; // Вызываем дружественный метод pointTestPoint.Set(&lineTestPoint); } Дружественные классыПо аналогии с дружественными функциями и методами, можно объявить дружественный класс. Все методы дружественного класса, могут обращаться ко всем элементам класса, включая элементы, объявленные как private и protect. Так, например, в предыдущем примере вы могли бы определить, что класс point является дружественным классу line. Все методы класса point могут обращаться к любым элемента класса line. //========================================================== // Класс point class point { // ... }; //========================================================== // Класс line class line { public: // Класс point объявляется дружественным классу line friend class point; }; |