- •Предисловие
- •Введение Эволюция разработки программного обеспечения
- •Технологии программирования
- •Основные понятия объектно-ориентированного программирования
- •Инкапсуляция
- •Свойства
- •Векторные свойства
- •Создание и уничтожение объектов
- •Конструкторы
- •Деструкторы
- •Наследование
- •Свойства
- •Конструкторы и деструкторы класса-предка
- •Полиморфизм, виртуальные и динамические методы
- •Статическое перекрытие виртуальных методов
- •Виртуальное перекрытие конструкторов и деструкторов
- •Абстрактные методы
- •Области видимости
- •Перекрытие и переопределение свойств
- •Перекрытие методов доступа к свойствам
- •Приведение объектных типов, операторы as и is
- •Агрегация
- •События
- •Процедурный тип
- •Создание события
- •Инициаторы события
- •Делегирование
- •Внутреннее устройство объекта
- •Указатели на класс
- •Виртуальные конструкторы
- •Методы класса
- •Обработка исключительных ситуаций
- •Операторы try...Except и try...Finally
- •Исключительные ситуации как объекты
- •Перегрузка методов
- •Перегрузка виртуальных методов
- •Параметры по умолчанию
- •Основы объектно-ориентированного анализа и проектирования
- •Объектно-ориентированная модель
- •Классы и объекты
- •Заключение Применение объектно-ориентированного программирования
- •Библиографический список
Методы класса
С указателем на класс тесно связано понятие особого типа методов – методов класса. Такие методы можно вызвать без создания объекта через имя класса, в котором они описаны. Хотя их можно вызывать и как обычные методы, через имя объекта. Перед описанием методов класса нужно поставить зарезервированное слово class.
Например:
Interface Type TMyObject = class(TObject) public class function GetSize: string; ... End;
Implementation Procedure Test; Var MyObject: TMyObject; AString: string; begin //вызываем метод класса GetSize через имя класса //без создания экземпляра объекта AString := TMyObject.GetSize; //создаем экземпляр объекта MyObject := TMyObject.Create; //вызываем метод класса GetSize как обычный метод AString := MyObject.GetSize; ... end;
Понятно, что методы класса не могут использовать значения, содержащиеся в полях класса, ведь при их вызове экземпляра класса, то есть набора полей, может еще не существовать.
Важнейшие методы класса определены в классе TObject – именно они позволяют, не углубляясь во внутреннюю структуру класса, извлечь оттуда практически всю необходимую информацию. Некоторые методы класса, определенные в классе TObject:
class function ClassName: ShortString;
- возвращает имя класса;
class function ClassNameIs(const Name: string): boolean;
- возвращает true, если имя класса равно заданному;
class function ClassParent: TClass;
- возвращает указатель на родительский класс;
class function ClassInfo: pointer;
- возвращает указатель на структуру с дополнительными данными об опубликованных методах и свойствах;
class function InstanceSize: longint;
- возвращает размер экземпляра класса;
class function UnheritsFrom(AClass: TClass): boolean;
- возвращает true, если данный класс наследует от заданного;
class function MethodAdress(const name: ShortString): pointer;
- возвращает адрес метода по его имени (только для опубликованных методов);
class function MethodName(Adress: Pointer): ShortString;
- возвращает имя метода по его адресу (только для опубликованных методов);
Рассмотрим пример:
Interface Type TMyObject = class ... end;
TMyObjClass = class of TObject;
Implementation Procedure Test; var AMyObjRef: TMyObjClass; S: string; begin AMyObjRef := TObject; S := AMyObjRef.ClassName; //в результате S = ‘TObject’ AMyObjRef := TMyObject; S:=AMyObjRef.ClassName; //в результате S = ‘TMyObject’ end.
В этом примере с помощью метода класса TObject.ClassName получается имя реального класса, на который указывает указатель AMyObjRef.
-
Статические элементы класса в С++
Механизм указателей на класс в Object Pascal позволяет рассматривать класс как некий объект, с которым можно работать – вызывать методы класса, создавать экземпляры объектов класса. В языке С++ нет специального типа «указатель на класс», тем не менее имеется возможность определять методы, вызываемые через имя класса и данные, общие для всех экземпляров одного класса. Это достигается за счет использования статических элементов класса.
Элементы класса можно объявлять со спецификатором static. В этом случае для всех объектов класса существует только один общий экземпляр этого элемента:
class CMyClass { ... public: int UnStaticVar; //обычное поле static int StaticVar; //статическое поле void SomeFunction(void); //обычная функция static void SomeStaticFunction(); //статическая функция };
Такие элементы класса называют статическими - не следует путать со статическим перекрытием методов.
Доступ к статическим элементам класса может выполняться без создания экземпляра класса через имя класса:
int i = CMyClass::StaticVar; CMyClass::SomeStaticFunstion();
Также можно обращаться к статическим элементам класса как к обычным, через имя объекта или указатель на объект:
CMyClass c; c.SomeStaticFunction();
но конкретный экземпляр объекта при этом не принимается во внимание.
Поскольку статические функции-элементы могут быть вызваны без указания конкретного экземпляра объекта, они не получают указателя this, и, таким образом, не могут получить доступ к нестатическим элементам класса за исключением случая, когда объект класса указывается явно. Со статическими полями данных статические функции-элементы могут работать без ограничений. Например:
class CMyClass { private ... int Data; //не статическое поле static int StaticData; //статическое поле public: static int GetData(CMyClass * ptr); //статическая функция };
int CMyClass:: GetData(CMyClass * ptr) { //return Data+StaticData; //ошибка, поскольку нет указания //экземпляра класса для нестатического поля данных
return ptr->Data+StaticData; //указали конкретный объект //для статического поля указывать ничего не надо }
Статические функции-элементы не могут быть виртуальными функциями. Недопустимо объявлять статические и не статические функции-элементы с одинаковым именем и типами аргументов.
Статические поля класса представляют собой данные, общие для всех экземпляров данного класса. Любой экземпляр класса может записывать или читать их значения. Объявление статических полей классов в объявлении класса не является определением статической переменной – то есть память под него не будет распределена автоматически. Следовательно, необходимо выполнить дополнительные действия для распределения памяти и инициализации статического поля.
Статическое поле глобального класса определяется и инициализируется подобно обычным глобальным переменным. Причем сделать это можно только на уровне файла:
class CMyClass { private ... static int StaticData; //объявление статического поля – //память не распределяется public: static int GetDataStatic(void); //статическая функция int GetData (void); //не статическая функция };
int CMyClass::StaticVar = 10; //определение статического поля – //память распределяется и выполняется инициализация //определение не может быть вложено в какую-либо функцию и т.п.
int CMyClass:: GetDataStatic(void) { return StaticData; //статическая функция работает //со статическим полем данных }
int CMyClass:: GetData(void) { return StaticData; //обычная функция работает со //статическим полем данных – для всех экземпляров //класса это поле будет общим. }
Статические элементы класса используются, чтобы организовать данные, общие для всех объектов одного класс, и обеспечить удобный доступ к ним. Это могут быть такие данные, как число созданных экземпляров класса, ссылки на совместно используемые ресурсы и т.п. Использование статических элементов класса позволяет:
-
уменьшить число глобальных имен;
-
сделать очевидным то, что статическая переменная логически принадлежит определенному классу;
-
выполнять контроль над доступом к данным.
-
С++ Шаблоны
Шаблоны – это механизм для организации параметризированных классов и функций. Предназначены для создания функций и классов, отличающихся только типом обрабатываемых данных.
Рассмотрим для примера функции поиска минимального из двух чисел разных типов. Такие функции могут быть описаны так:
//минимум для целых чисел int int Min( int a, int b) { return (a < b) ? a : b; };
//минимум для вещественных чисел double Min(double a, double b) { return (a < b) ? a : b; };
//минимум для символьного типа char Min(char a, char b) { return (a < b) ? a : b; };
Видно, что действия функций одинаковы, и все различие состоит в типе данных, с которыми они работают.
С помощью механизма шаблонов можно создать для них единый шаблон, который будет определен как:
template <class T> T Min(T a, T b) { return (a < b) ? a : b; };
Когда функция, определенная как шаблон, впервые вызывается с параметром какого-либо типа, компилятор создает полностью определенную функцию, являющуюся версией функции-шаблона для данного типа. Далее при каждом вызове с этим типом параметров будет вызываться эта функция – то есть будет создана только одна копия функции для каждого варианта типов.
Формат определения шаблонов классов и функций следующий:
template < [typelist] [, [ arglist ]] > declaration
где typelist – список разделенных запятой имен типов, которые будут использоваться в определении, arglist – список аргументов, declaration – объявление функции или класса – как обычных.
Таким образом, шаблоны функции или класса, называемые также параметризованными функциями и классами, представляют собой определение функции или класса, где один или несколько типов данных и значений аргументов объявлены как параметры. Определяя для этих параметров конкретные значения типов и величин, можно породить соответствующие функцию или класс, работающие с данными этих типов и имеющих установленные значения аргументов. С помощью значений аргументов можно, например, определить число элементов массива. Функции или классы, для которых задано значение параметров, называют инстацированными (наполненными или конкретизированными).
Пример создания и использования шаблона класса:
//параметризованный класс: template <class T, int i> class CTestClass //параметры – тип T и аргумент i { T arr[i]; public: void fin(int,T); T fout(int); };
//методы параметризованного класса также являются //параметризованными: template <class T, int i> T CTestClass<T,i>::fout(int i) { return arr[i]; };
template <class T, int i> void CTestClass<T,i>::fin(int i, T t) { arr[i] = t; };
Для инстацирования и создания экземпляра класса:
CTestClass<double, 5> ClassInst; //массив из пяти //вещественных чисел CTestClass<int, 3> ClassInst1; //массив из трех целых чисел
Использование объектов инстацированного класса ничем не отличается от работы с объектами обычных классов:
double a = 2.1, b; ClassInst.fin(1,a); ClassInst1.fin(1,a); b = ClassInst.fout(1); b = ClassInst1.fout(1);
Используя механизм шаблонов С++, легко создавать классы контейнеров, которые будут работать с широким кругом типов данных – как встроенных в язык, так и создаваемых программистом. Подобные контейнеры, реализующие массивы, списки и т.п. входят в состав многих библиотек С++.