Введение в программирование

         

Файлы


Файл представляет собой упорядоченный набор элементов одного типа.

Стандартные процедуры ввода и вывода используют предопределенный тип TextFile, или Text, который реализует файл как упорядоченный набор строк символов.

Объявление типа файл и создание переменных файлового типа имеет следующее формальное описание:

type тип_файла = file of тип; var идентификатор: file of тип_файла;

Тип, указываемый после ключевых слов file of, может быть производным типом или базовым типом. Так, в качестве типа можно указать string[число_символов] или array[нач_инд..кон_инд] of Char.

Объявление нетипизированного файла имеет следующее формальное описание:

var идентификатор: file;

Для создания переменной файлового типа следует предварительно объявить файловый тип, а затем объявить переменную созданного типа, или сразу объявить переменную, указав используемый тип как файловый.

Например:

type Phone = record Name: string[30]; PhoneNumber: string[20]; end; {Объявление файлового типа } PhoneList = file of Phone; var {Объявление переменной файлового типа} book1: PhoneList; {Объявление переменной с указанием типа как файлового типа} book2: file of Phone;


Массивы


В языке Object Pascal можно создавать одномерные и многомерные массивы. Все элементы массива имеют одинаковый тип.

Одномерный массив языка Object Pascal имеет следующее формальное описание:

Идентификатор: array [нач_индекс..кон_индекс] of тип_массива;

Например:

type RealArr = array[1..100] of Real; var int1: array[1..20] of Integer; int2: array[1..20] of Integer; int3, int4: array[1..10] of Integer; real1: array[1..10] of Real; begin Int1 := Int2; end.

Массивы могут объявляться как константы.

Например:

type TCub = array[0..1, 0..1, 0..1] of Integer; const cub1: TCube = (((0, 1), (2, 3)), ((4, 5), (6,7))); { Элементы массива cub1 будут содержать следующие значения: cub1 [0,0,0] = 0 cub1 [0,0,1] = 1 cub1 [0,1,0] = 2 и т.д. }

Массив символов объявляется как массив переменных типа Char. Символьный массив может быть доступен через указатель на массив типа PChar.

Строки можно реализовывать как символьные массивы, ограниченные 0-символом (#0).

Например:



const TempString: array[0..8] of Char = 'Строка'#0; var P: PChar; {Указатель на строку} begin P := 'Строка'; P := @TempString; {Переменная P указывает на значение 'Строка'} end.

Массив символов типа Char и значение типа PChar эквивалентны.

Например:

var MyArray: array[0..5] of Char; MyPointer: PChar; begin MyArray := 'array'; MyPointer := MyArray; MyProc(MyArray); {Эквивалентно вызову MyProc(MyPointer);} end.

Многомерный массив имеет следующее формальное описание:

array[диапазон_первого_индекса, ..., диапазон_n_индекса] of базовый_тип; array[диапазон] of array[диапазон] of array[диапазон] of тип;

Так, объявление

type MyMassiv = array[1..10] of array[1..70] of Integer;

эквивалентно объявлению

type MyMassiv = array[1..10, 1..70] of Integer;

Например:

type MyAr = array[1..10, 1..50] of Real; begin MyAr[1,49]:= 49; end.

По принципу выделения памяти под массив массивы языка Object Pascal делятся на статические и динамические.

Память под статический массив выделяется при его создании.

Динамический массив не имеет фиксированного размера или длины.


При объявлении динамического массива память под него не выделяется. Выделение памяти происходит каждый раз при присвоении значения массиву или при вызове процедуры SetLength.

Создание динамического массива имеет следующее формальное описание:

array of тип_элементов_массива;

Например:

{Объявление динамического массива} var MyAr: array of Integer; {Определение количества элементов в динамическом массиве:} SetLength(MyAr, 10);

Элементы динамического массива всегда индексируются с 0. Переменная типа динамического массива является указателем, и к ней применимы операции над указателями. Для освобождения памяти, занимаемой динамическим массивом, можно присвоить значение nil переменной, ссылающейся на массив. Количество элементов динамического массива определяется процедурой SetLength. Функция Length возвращает количество элементов в динамическом массиве.

При сравнении двух переменных типа динамического массива выполняется сравнение их ссылок, а не значений их элементов. Например:

var A, B: array of Integer; begin SetLength(A, 1); SetLength(B, 1); A[0] := 2; B[0] := 2; {A[0] = B[0] вернет значение True} {A = B вернет значение False} end.

Для усечения динамического массива можно использовать функцию Copy.

Например:

var A: array of Integer; begin SetLength(A, 50); {Оставляет 10 элементов массива A} A := Copy(A, 0, 10); end.

Если при объявлении функции или процедуры формальный параметр определяется как array of тип_массива, то эта функция или процедура может получить в качестве фактического параметра любой динамический или статический массив указанного типа вне зависимости от его размера и индексации его элементов.


Множества


язык Object Pascal предоставляет два простых типа, описывающих множества значений. Это:

порядковый тип - задающий множество значений внутри указанного интервала; перечислимый тип - задающий множество значений, перечисленных внутри фигурных скобок и через запятую.

Множество языка Object Pascal - это набор значений порядкового или перечислимого типа. Множество определяется ключевым словом set of.

Создание порядкового и перечислимого типов имеет следующее формальное описание:

Идентификатор = Начальное_значение .. Конечное_значение; Идентификатор = {Значение_i .,...}; Идентификатор = Значение_i..Значение_j; Идентификатор = set of порядковый или перечислимый тип.

Например:

type {Объявление перечислимого типа} TMyColor = {Red,Blue,Yellow}; {Объявление типа множество} TMyClr = set of TMyColor; var {Объявление переменных созданных типов} ValC1, ValC2: TMyClr; begin {Присвоение значения переменной типа множество} ValC1 := [Blue]; ValC2 := ValC1+[ Red]; end.

Начальным и конечным значением интервала для порядкового типа могут быть:

символы из кодовой таблицы ASCII (например, цифры или буквы); любой диапазон значений ранее объявленного перечислимого типа.

Перед работой с переменной множественного типа ее следует проинициализировать. Значения инициализации указываются в квадратных скобках.

Например:

type TMyInts = 1..500; TIntSet = set of TMyInts; {Объявление переменной типа множества} var set1: TIntSet; begin {Инициализация переменной типа множества} set1:=[1,2,3,4]; end.

Над множеством допустимы следующие операции:

объединение множеств (+); разность (-); умножение (*); сравнение (<=, >=, =, <>); принадлежность множеству (in).

Например:

type {Порядковый тип: значения от 1 до 9} CountType = 1..9; LowerCharType = а..я; AType = (A1, A2, A3, A4, A5); {Порядковый тип: значения A3, A4 и A5} AType3 = A3..A5; {Множество на основе порядкового типа} ATypeSet = set of AType3; {Множество LetterType } LetterType = set of 'A'..'Z'; var {Объявление переменной типа множества} aset: ATypeSet; begin {Присвоение значения переменной множественного типа} aset:= [A3] + [A4]; {Вычитание множеств} aset:= aset - [A4]; end.

Каждая переменная множественного типа может иметь значение - множество. Для изменения значений переменной множественного типа могут использоваться операции объединения (+) и разности (-) множеств.

Например:

begin set1:=set1 +[5,6]; end.

Для определения принадлежности значения некоторому множеству используется операция in.

Например:

begin if 5 in set1 then ShowMessage( 'Значение принадлежит множеству set1'); end.



Объявление производного типа


Кроме базовых типов данных, рассмотренных в лекции 6, язык Object Pascal предоставляет простые и структурированные типы данных.

Используя базовые, простые и структурированные типы данных, можно создавать производные типы данных.

Напомним, что к базовым типам относятся:

целочисленный тип; действительный тип; логический тип; символьный тип; строковый тип.

К простым типам относятся:

порядковый тип; перечислимый тип.

К структурированным типам относятся:

множества; массивы; записи; файлы; объектный тип (тип класса); тип ссылки на класс.

Для того чтобы создать переменную производного типа, сначала следует определить этот производный тип. Объявление нового производного типа указывается ключевым словом type.

Объявление произвольного типа как нового имени существующего типа в языке Object Pascal имеет следующее формальное описание:

type имя_нового_типа = имя_существующего_типа;

Объявление произвольного типа как нового типа в языке Object Pascal имеет следующее формальное описание:

type имя_нового_типа = type имя_существующего_типа;

При объявлении произвольного типа как нового имени типа, тип переменных производного типа и типа, используемого для его создания, будет совпадать. Если перед именем существующего типа указать ключевое слово type, то компилятор создаст новый тип. При этом указание в качестве формального параметра подпрограммы переменной существующего типа, а в качестве фактического параметра подпрограммы - переменной нового типа, созданного на базе данного существующего типа, вызовет для var-параметров ошибку времени компиляции.

В языке Object Pascal объявление типов и переменных может быть выполнено только в определенных местах программы: секция объявления типа type используется для объявлений типов, а секция var для объявления переменных.

Например:

type TValReal1 = Real; var // Переменные X и Y имеют // один и тот же тип X: Real; Y: TValReal1; Е type TValReal2 = type Real; var // Переменные X и Y имеют разные типы X: Real; Y: TValReal2;



Записи


Запись представляет собой структуру, состоящую из набора полей различных типов. Каждый элемент записи называется полем. Каждое поле имеет свой идентификатор и свой тип.

Запись может иметь поля двух видов:

фиксированные поля, которые всегда присутствуют в создаваемом экземпляре записи; вариантные поля (располагаются только после фиксированных полей), для которых память при создании экземпляра записи выделяется только под одно самое наибольшее поле из варианта.

Объявление типа записи завершается ключевым словом end;.

Например:

type MyDateRec = record Year: Integer; Month: 1..12; Day: 1..31; end; var {Объявление переменных} Record1, Record2: MyDateRec; begin {Доступ к полям записи: } Record1.Month := 11; Record1.Day := 22; {Доступ к полям записи:} with Record1 do begin Year := 2004; Month := 11; Day := 22; end; {Копирование значений полей записей одного типа } Record2:= Record1;

Переменная типа записи может быть объявлена одновременно с определением полей записи.

Например:

var S: record Name: string; Age: Integer; end;

Запись, состоящая из одних вариантных полей, реализует тип, в некотором роде аналогичный типу union для языка С++.

Вариантные поля позволяют на основе одного типа записи создавать экземпляры записей, содержащие поля разных типов. Это позволяет значительно экономить память.

Объявление записи имеет следующее формальное описание:

type имя_типа_записи = record список_полей: тип; {фиксированные поля} ... case тег: тип_варианта of {вариантные поля} константа_или_список: (вариант_1); ... константа_или_список: (вариант_n); end;

Описание каждого фиксированного поля состоит из его идентификатора (или списка идентификаторов) и типа. Описания полей разделяются точкой с запятой. Вариантная часть объявления записи начинается с зарезервированного слова case.

При определении вариантной части тег и символ двоеточия могут быть опущены в том случае, если тип_варианта описывает существующий тип или каждая константа (константа_или_список) в вариантной части является значением типа тип_варианта.

Тип варианта не может быть длинной строкой, динамическим массивом, типом Variant или интерфейсом, но он может быть указателем на эти типы.

Например:

type TPerson = record FirstName, LastName: string[40]; case bl: Boolean of {Вариантные поля} True: (str1: string[40]); False: (str2: string[20]; date1: TDate); end; TShapeList = (Rectangle, Circle, Other, Ellipse); TFigure = record case TShapeList of Rectangle: (Height, Width: Real); Circle: (Radius: Real); Ellipse, Other: (); end;

Применяя вариантные поля, можно одинаково трактовать данные различных типов, используя для доступа к ним вариантные поля соответствующих типов.



Конструкторы класса


Конструктором называется метод, вызываемый при создании объекта данного класса. Класс может иметь несколько конструкторов, отличающихся списком параметров.

Деструктором называется метод, вызываемый при разрушении объекта данного класса. Имена конструктора и деструктора совпадают с именем класса, но перед именем деструктора указывается символ ~.

При создании объектов последовательно вызываются конструкторы всех его базовых классов. Вызов деструкторов при уничтожении объекта происходит в обратном порядке.

Если конструктор базового класса имеет список параметров, то для его использования в производном классе следует создать конструктор производного класса с таким же списком параметров.

При создании нового класса среда Visual C++ автоматически создает заголовочный файл и файл реализации класса с конструктором и деструктором без параметров.



Квалификация имен


Квалификация имен используется для однозначного понимания указываемого имени.

Для квалификации имени могут использоваться следующие операторы: :: (оператор принадлежности); . (оператор доступа к члену класса посредством имени); -> (оператор доступа к члену класса через указатель).


Объектно-ориентированное программирование на языке С++


В настоящее время понятие языка программирования неотрывно связано со средой программирования, в которой разрабатываются приложения. Для языка С++ наиболее развитыми и популярными средами программирования являются:

Visual Studio.NET;С++ Builder.

Среда программирования Visual Studio.NET предназначена для создания приложений не только на языке С++, но и на таких популярных языках, как C# и Visul Basic. Иногда для сокращения говорят, что проектирование приложений на C++ в Visual Studio.NET реализуется средой Visual C++.

Visual C++ позволяет разрабатывать приложения как в терминах традиционного модульного программирования, так и с применением объектно-ориентированного программирования.



Объектные типы


Тип данных всегда определяет размер памяти, которая будет выделена под переменную данного типа при ее создании.

При объявлении переменной объектного типа (типа класса) создаются переменные члены класса и вызывается конструктор класса. Производные типы на основе классов позволяют получать доступ к членам класса.

Переменная, объявленная как указатель на класс, применяется для доступа к методам и переменным членам класса. В следующей таблице приведен синтаксис, используемый при работе с указателями на члены класса.

Hапример:

#include <iostream.h> class A // Объявление класса A { public: int j1; A(void); ~A(void);}; void main() { int A::*pToj = &A::j1; // Объявление производного типа pToj // как указателя на член класса j1 A jA; // Объявление переменной объектного типа A A *pjA = & jA; // Объявление указателя на эту // переменную (объект) int j; jA.*pToj = 123; // Присваивание значения переменной // jA::j1, используя * j = pjA ->*pToj; // Получение значения, используя ->* std::cout << j << "\n"; }



Объявление и реализация класса в языке С++


Создаваемый класс должен быть объявлен и реализован.

Объявление класса в языке С++ может иметь следующее формальное описание:

class имя_класса : список_базовых_классов { public: // Модификатор доступа относится // ко всем перечисленным после // него членам до следующего // модификатора доступа // Объявление общедоступных членов класса

protected: // Объявление членов класса, доступных // только для производных классов

private: // Объявление защищенных членов класса };

Список базовых классов указывается после имени класса через символ двоеточия (:), разделяется запятыми и может иметь модификаторы доступа.

Например:

class MyClass : public ClassA, public ClassB, private ClassC {};

В языке С++ считается, что если модификатор доступа для класса или члена класса не указан, то по умолчанию предполагается модификатор доступа private (защищенный доступ). Для членов структур, объявляемых ключевым словом struct, по умолчанию модификатор доступа предполагается равным public.

Модификатор доступа базового класса позволяет определить, какие переменные и методы базового класса будут доступны из производного класса. Модификатор доступа, указываемый перед именем базового класса, определяет следующие правила доступа к переменным и методам базового класса из производного класса:

public - в производном классе доступны все переменные и методы базового класса с модификаторами доступа public и protected, и эти члены класса имеют те же права доступа;protected - члены базового класса с модификаторами доступа public и protected доступны как protected, а с модификатором доступа private - недоступны.private - члены базового класса с модификаторами доступа public и protected доступны как private, а с модификатором доступа private - недоступны.

Например:

class BaseClass { public: int PublicFunc(); protected: int ProtectedFunc(); private: int PrivateFunc(); }; class DerivedClass1 : public BaseClass { // Наследуемая функция PublicFunc доступна // как public // Наследуемая функция ProtectedFunc // доступна как protected }; class DerivedClass2 : private BaseClass { // Наследуемая функция PublicFunc доступна // как private // Наследуемая функция ProtectedFunc // доступна как private }; int main() { }

В теле объявления класса указываются модификаторы доступа, описывающие права доступа для переменных и методов класса:

модификатор доступа относится ко всем перечисленным после него членам до следующего модификатора доступа;один и тот же модификатор доступа может указываться несколько раз;после модификатора доступа ставится символ двоеточие;если модификатор доступа не указан, то по умолчанию предполагается private.

Для доступа к членам класса используется операция принадлежности ::, указываемая после идентификатора класса. Для доступа к членам экземпляра класса используются операции . и ->.

Для доступа к объекту самого класса внутри метода члена класса используется ключевое слово this.

Например:

class A { public: int i; Func1();} A:: Func1() { return this->i; } // this - указатель класса A



Преобразование объектных типов


Указатель на класс может быть преобразован к указателю на базовый класс в двух случаях:

если базовый класс является доступным и преобразование однозначно;если указано явное преобразование типа указателя.

Например:

class A // А - базовый класс {public: int a1; int Fa(); }; class B : public A // В - производный класс {public: int b1; int Fb(); }; B bObj; // Объект типа B A *pA = &bObj; // Преобразование типа ограничивает // доступ к членам класса B и // разрешает доступ только к членам // базового класса B *pB = &bObj; // Нет преобразования типа и // следовательно разрешен доступ как // к членам класса B, // так и к членам класса А. pA-> Fa (); // Правильно: вызов функции члена класса A. pB-> Fa (); // Правильно: вызов функции, //наследуемой от класса A // Вызов pA-> Fb (); ошибочен: функция Fb // недоступна через указатель на базовый // класс А

Указатели на члены класса или структуры не могут рассматриваться как обычные указатели и для них не выполняется стандартное преобразование типа.



Создание объекта


Для создания объекта (экземпляра данного класса) следует объявить переменную типа указатель на класс, а затем создать объект, выполнив оператор new с указанием используемого конструктора.

Например:

A* ca; ca= new A();

Эти же действия можно записать одним оператором.

Например:

A* ca= new A();

Для того чтобы можно было использовать конструктор с параметрами, значения параметров необходимо указать при создании объекта.



Структура приложения на языке С++


Приложение состоит из модулей трансляции - файлов исходного кода на языке С++. Каждый модуль трансляции представляется файлом реализации класса и заголовочным файлом (компилятор позволяет записывать всю информацию - объявление и реализацию, в один файл, но это оправданно только для маленьких программ).

Проектом в терминологии Visual C++ называется совокупность всех модулей трансляции, файлов ресурсов и описания конфигурации.

Разработка любого приложения в Visual C++ начинается с создания проекта. Visual Studio.NET предоставляет шаблоны для создания различных видов приложений (консольные приложения, MFC-приложения, DLL-библиотеки, приложения управляемого кода и т.п.).

Консольное приложение - это приложение, не использующее Windows-окна для обработки сообщений от пользователя. Точкой входа в консольное приложение в языке С++ является метод main.

После того как создан шаблон приложения заданного вида, информация обо всех файлах проекта отображается в окне проектов среды Visual C++.

Заголовочный файл содержит объявления используемых данных. Язык C++ поддерживает соглашение о раздельной компиляции: каждый С++-модуль можно компилировать отдельно. Для того чтобы несколько модулей могли использовать одни и те же данные, объявление этих данных выносят в заголовочный файл. Принято, что имя заголовочного файла имеет расширение h.

Все подключаемые к модулю заголовочные файлы указываются в начале модуля директивой препроцессора #include.

Например:

#include "stdafx.h" #include <iostream>

Имя подключаемого файла в директиве препроцессора #include может быть указано:

в двойных кавычках - в этом случае препроцессор сначала выполняет поиск данного файла в том же каталоге, в котором расположен файл, содержащий директиву препроцессора #include, а затем в каталогах, определяемых опцией компиляции и переменной среды INCLUDE.в угловых скобках - при этом исключается поиск имени файла в том же каталоге, в котором расположен файл, содержащий директиву препроцессора #include.

Файл, содержащий реализацию методов, объявленных в заголовочном файле, иногда называется исходным файлом.

Каждая программа должна иметь точку входа и может содержать описание одного или нескольких классов.

Точкой входа в программу в приложении, формируемом по шаблону в среде Visual C++, является метод _tmain, заменяемый при компиляции на метод main.

Минимальной программой на С++ является следующий код:

int main() {}

Эта программа определяет функцию с именем main, которая не использует параметров и не выполняет никаких действий.

Тело метода в языке С++ указывается в фигурных скобках. Перед именем метода указывается тип возвращаемого значения. Если метод не возвращает никакого значения, то его тип обозначается ключевым словом void.



Терминология объектно-ориентированного программирования


Объектно-ориентированное программирование позволяет оперировать в терминах классов: определять классы, конструировать производные классы, создавать объекты, принадлежащие классу, - экземпляры класса.

Сначала в некоторых языках программирования появился тип struct, расширением которого стал тип class.

Класс определяет данные (переменные) и поведение (методы). Данные и методы класса также называют членами класса. Класс рассматривается как определяемый пользователем тип данных.

Объектом называется экземпляр некоторого класса. Объект создается как переменная типа класса, которая используется для доступа к данным - членам класса и для вызова методов - членов класса.

Наследованием называется механизм, позволяющий производному классу наследовать структуру данных и поведение другого класса, а также наследовать поведение, объявленное в интерфейсах и абстрактных классах.

Наследование позволяет определять новые классы в терминах существующих классов.

В объектно-ориентированном программировании наследование может быть:

множественным, позволяющим производному классу наследоваться одновременно от нескольких классов (например, так реализован механизм наследования в С++);простым, когда производный класс имеет только один наследуемый класс (например, так реализованы языки Java и Object Pascal).

Наследуемый класс принято называть базовым классом, или родительским классом (классом - предком, суперклассом).

Производный класс, наследующий структуру данных и поведение своего базового класса, иногда также называется дочерним классом (классом - потомком, подклассом).

В производном классе можно переопределять методы базового класса и добавлять новые методы. Непосредственным базовым классом называется класс, от которого порожден производный класс следующего уровня иерархии:

   А   Базовый класс класса С и непосредственный базовый класс класса B
   ?    
   B   Непосредственный базовый класс класса C
   ?    
   C   Производный класс

Полиморфизмом называется способность различных объектов по-разному обрабатывать одинаковые сообщения.

Инкапсуляция позволяет работать в терминах объектов и скрывать их переменные и методы. Использование инкапсуляции дает возможность модифицировать внутреннюю реализацию объекта без влияния на программу в целом до тех пор, пока не изменяется интерфейс с объектом.

В языках программирования инкапсуляция поддерживается реализацией модификаторов доступа, таких как protected - для защищенных членов класса на уровне класса, и private - для полностью защищенных членов класса.



Вложенные классы


Язык С++ допускает использование вложенных классов - внутри тела одного класса содержится объявление других классов.

Например:

class A { public: A(void); ~A(void); class B { // Вложенный класс B(void) {}; ~B(void) {}; char sStr2[3]; };

};



Дружественные члены класса


Дружественные члены класса (методы) позволяют получить доступ к защищенным модификатором private членам класса из методов других классов. Методы и классы, объявляемые дружественными, иногда также называются друзьями класса.

Если метод класса A внутри тела класса B объявляется с модификатором friend, что указывает на то, что он является другом класса, то из него разрешен доступ ко всем членам класса B.

Например:

class A { public: int Fx();} class B { public: friend int A::Fx(); private: }



Дружественные классы


Объявление дружественного класса позволяет всем его методам получить доступ ко всем переменным и методам другого класса.

Например:

class A {public: int Fx();} class B {public: friend class A; private: }

Дружественный класс или член класса будет доступен только в том случае, если он был объявлен в области видимости самого класса или ранее во внешней области видимости, внутри которой располагается область видимости, содержащая объявление класса с объявлениями друзей класса.

Например:

class A {public: // Класс расположен во внешней // области видимости int Fx1(); } namespace classB { class B {public: friend class A; friend class C; private: } class C { public: // Класс расположен в том же // пространстве имен int Fx2(); } }

Дружественные классы не наследуются, и их дружественность не является транзитивной.

Например:

class A {int Fx();} class B {friend class A;} class C {friend class B;} // Класс А не является // дружественным классу C class D : public B {} // Класс А не является // дружественным классу D


Методы-члены класса


В терминологии объектно-ориентированного программирования функции также называются методами или методами - членами класса, для того чтобы подчеркнуть, что конкретная функция является членом некоторого класса.

По стандарту ANSI C, используемому компилятором Visual C++, любая используемая в модуле компиляции функция должна иметь прототип, включающий в себя тип функции, имя функции и список параметров с их типами. Прототип - это некоторое предварительное описание функции, заканчивающееся символом ;.

Прототип функции должен быть вставлен во все модули компиляции, использующие функцию. Для небольших программ прототип функции обычно записывается в модуль компиляции. Для программ, создаваемых средствами Visual C++, и программ, состоящих из нескольких модулей, прототип функции указывается в заголовочном файле. Объявление функции должно однозначно соответствовать ее прототипу и может находиться в любом модуле компиляции (для которого доступен прототип функции).

Прототип метода, являющегося членом некоторого класса, записывается в заголовочном файле, содержащем объявление класса в теле этого класса.

Метод - член класса может иметь следующие модификаторы доступа:

public - общедоступный метод;protected - метод, доступный только для членов и объектов данного класса и наследуемых классов (наследуемых с модификаторами доступа public или protected);private - защищенный метод, доступный только внутри класса.

Прототип метода может иметь следующее формальное описание:

модификатор_доступа тип_метода имя_метода (список_параметров);

Объявление метода может иметь следующее формальное описание:

модификатор_доступа тип_метода имя_метода (список_параметров) { тело_метода }

Тип метода является типом возвращаемого методом значения и может быть любым допустимым базовым или производным типом, включая и тип указателя.

Указатели типа void могут использоваться для объявления параметров метода в том случае, если тип этих параметров на момент компиляции неизвестен.

Например:

void Fx(void *pV); //Прототип метода ...
piVar=new int; pfVar=new float; Fx(piVar); // Вызов метода для // параметра типа int Fx(pfVar); // Вызов метода для // параметра типа float ... // Реализация метода void Fx(void *pV) {*pV=12345;}

Метод, возвращающий указатель, записывается следующим образом:

тип *имя_метода(список_параметров).

Список формальных параметров - это значения, передаваемые в функцию. Каждый элемент этого списка может иметь любой допустимый тип.

Язык C++ допускает передачу параметров по значению и по ссылке или указателю. Параметры, передаваемые по значению, при выполнении программы заносятся в стек, и для больших размеров структур или массивов может возникнуть ситуация переполнения стека. Параметры, передаваемые как ссылки или указатели, не копируются в стек.

Изменение значений самих параметров происходит только для параметров, переданных по ссылке или указателю. При передаче параметра по значению метод изменяет не сам параметр, а его копию.

Список параметров может включать параметры, имеющие значения по умолчанию. Такие параметры указываются в конце списка параметров. Значение по умолчанию будет использовано только в том случае, если число переданных параметров меньше числа указанных формальных параметров в описании метода.

Язык C++ разрешает рекурсивный вызов методов.

В методах класса можно использовать ключевое слово this, являющееся указателем на объект данного класса. Это ключевое слово нельзя использовать вне метода члена класса.


Методы с переменным числом параметров


Метод может иметь переменное число параметров: это указывается в конце списка параметров символом многоточия ... . Методы с переменным числом параметров должны иметь как минимум один описанный формальный параметр.

Для применения переменного числа параметров можно использовать:

макрос va_start(указатель_списка_параметров,последний_параметр) - при инициализации указателя и установке его на начало переменного списка параметров;макрос va_arg(указатель_списка_параметров,i) - при выборе параметра и продвижении указателя в соответствии с его типом на следующий параметр;макрос va_end((указатель_списка_параметров) - при установке указателя на список параметров, равный null, после извлечения значений всех параметров;тип va_list - для создания указателя на список параметров.

Например:

#include <stdio.h> // Библиотека, содержащая тип va_list #include <varargs.h> #include <iostream> int Fx ( int i1, ... ); // Прототип Функции с переменным // числом параметров void main( void ) { // Вызов функции Fx с различным // числом параметров std::cout<< Fx ( 100, 200) ); std::cout<< Fx ( 5 ) ); std::cout<< Fx ( -1,-3,45 ) ); } int Fx ( int i1, ... ) // Реализация функции с переменным // числом параметров { int sumx,i; sumx = 0, i = i1; va_list list1; // Список параметров va_start(list1, i1 ); // Инициализация списка параметров while( i != -1 ) { sumx += i; i = va_arg(list1, int); //Извлечение следующего параметра } va_end(list1); return( sumx ); }



Перегрузка функций и методов


Язык C++ позволяет выполнять перегрузку функций и методов - членов класса, то есть вызывать функцию (метод) с тем же именем, но с различными типами фактических параметров. Для создания перегружаемой функции следует указать отдельный прототип и сделать отдельное объявление для каждого списка параметров. Если функции различаются только типом и имеют совпадающие списки параметров, то для таких функций перегрузка не допускается.

Для определения вызываемой перегружаемой функции учитываются только списки параметров, а тип возвращаемого значения не учитывается. При этом для соответствия списка параметров вызываемой функции может применяться преобразование типов и соответствие по многоточию (соответствие по многоточию?) для функций с переменным числом параметров.



Создание метода-члена класса


Для того чтобы в среде Visual Studio .NET добавить в класс новый метод - член класса, следует в окне просмотра классов Class View выделить секцию с именем класса и выполнить команду контекстного меню Add|Add Function, а затем в диалоге - мастере построения метода ввести имя метода, его тип, а также определить список параметров.

При добавлении метода - члена класса в файл реализации класса автоматически добавляется код реализации нового метода, а в заголовочный файл - объявление этого метода.



Указатели на методы-члены класса и на функции


Указатели на методы и на функции могут быть использованы для передачи метода в качестве параметра другому методу.

Объявление указателя на метод может иметь следующее формальное описание:

тип_метода (имя_класса::*имя_метода_указателя) (список параметров); тип_функции (*имя_ функции_указателя) (список параметров);

Инициализация указателя может иметь следующее формальное описание:

тип_метода имя_класса::*имя_метода_указателя (список параметров)= &имя_класса::имя_любого_метода; тип_функции (*имя_ функции_указателя) (список параметров)= &имя_функции;

Вызов метода, объявленного как указатель на метод, может быть выполнен следующим образом:

(имя_объекта->*имя_ метода_указателя) (список параметров); (*имя_ функции_указателя)(список параметров);

Для функций, но не для методов - членов класса, разрешена другая форма вызова метода:

имя_ функции_указателя(список параметров); (имя_ функции_указателя)(список параметров);

Объявление функции, имеющей в качестве параметра указатель на метод, может иметь следующее формальное описание:

тип_метода имя_метода (тип_метода_указателя (*имя_метода_указателя) (список параметров));

Вызов метода, использующего в качестве параметра указатель на метод, может иметь следующее формальное описание:

имя_метода(имя_объекта->*имя_метода_указателя); имя_функции(имя_функции_указателя);

Разрешается создавать массив указателей на функции.

При использовании указателей на функцию можно не употреблять операцию разыменования или операцию получения адреса.

Например:

class a1 { public: a1(void); ~a1(void); int Fx1(int i1); int Fx2(int i2); }; a1::a1(void){} a1::~a1(void){} int a1::Fx1(int i1){ return 1;} int a1::Fx2(int i2){ return 2;}

int (*Fy_pointer)(); // Объявление указателя на функцию int Fy (); int _main(int argc, char* argv[]) { a1* a1Object = new a1(); int (a1::*Fx_pointer)(int)=&a1::Fx2; // Объявление и инициализация указателя // на метод - член класса int i; i=(a1Object->*Fx_pointer)(1); // Вызов по указателю на метод std::cout<<i; int (*Fy_pointer)()=&Fy; // Объявление и инициализация указателя // на функцию std::cout<<(Fy_pointer)(); std::cout<<(*Fy_pointer)(); // Вызов по указателю на // функцию (две формы) return 0; } int Fy () {return 5;}



Встроенные функции


Встроенные функции указываются оператором inline. Применение встроенных функций может несколько сократить время выполнения программы, так как компилятор встраивает код такой функции в том месте программы, где указан ее вызов.

Например:

inline void Fx(void) { std::cout<<"Функция Fx"<<std::endl;} ... Fx(void); // Этот код будет заменен компилятором // на код встроенной функции



Абстрактные классы


Абстрактным классом называется класс, который содержит хотя бы одну чисто виртуальную функцию.

Абстрактный класс не может быть явно использован для создания объектов.

Как правило, абстрактный класс применяется для описания интерфейса, который должен быть реализован всеми его производными классами.

Если класс, производный от абстрактного класса, не содержит реализации всех его чисто виртуальных функций, то он также является абстрактным классом.



Указатель на объект класса


class A // Объявление базового класса { public: virtual void VirtMetod1(); // Виртуальный метод void Metod2(); // Не виртуальный метод }; void A::VirtMetod() { cout << "Вызван A::VirtMetod1\n";}

void A::Metod2() { cout << "Вызван A::Metod2\n"; }

class B : public A // Объявление производного класса {public: void VirtMetod1(); // Виртуальный метод void Metod2(); // Не виртуальный метод }; void B::VirtMetod1() { cout << "B::VirtMetod1\n";} void B::Metod2() { cout << "B::Metod2\n"; } void main() { B aB; // Объект класса B B *pB = &aB; // Указатель на объект класса B A *pA = &aB; // Указатель на объект класса A pA->VirtMetod1(); // Вызов метода VirtMetod класса B pB->VirtMetod1(); // Вызов метода VirtMetod класса B pA->Metod2(); // Вызов метода Metod2 класса A pB->Metod2(); // Вызов метода Metod2 класса B }

Пример 12.1. Вызов виртуальных методов
Закрыть окно





class A // Объявление базового класса

{ public:

virtual void VirtMetod1(); // Виртуальный метод

void Metod2(); // Не виртуальный метод

};

void A::VirtMetod() { cout << "Вызван A::VirtMetod1\n";}

void A::Metod2() { cout << "Вызван A::Metod2\n"; }

class B : public A // Объявление производного класса

{public:

void VirtMetod1(); // Виртуальный метод

void Metod2(); // Не виртуальный метод

};

void B::VirtMetod1() { cout << "B::VirtMetod1\n";}

void B::Metod2() { cout << "B::Metod2\n"; }

void main() {

B aB; // Объект класса B

B *pB = &aB; // Указатель на объект класса B

A *pA = &aB; // Указатель на объект класса A

pA->VirtMetod1(); // Вызов метода VirtMetod класса B

pB->VirtMetod1(); // Вызов метода VirtMetod класса B

pA->Metod2(); // Вызов метода Metod2 класса A

pB->Metod2(); // Вызов метода Metod2 класса B

}


Реализация методов шаблона класса template


template <class T1> // Описание шаблона класса class A { T1 x,y; public: A(); ~A(); T1 Fx(); void Fy(T1 a, T1 b); }; template <class T1> A< T1>::A() {} // Реализация методов шаблона класса template <class T1> A< T1>::~A() {} template <class T1>T1 A< T1>::Fx(){ return x*y;} template <class T1>void A< T1>::Fy(T1 a, T1 b) {x=a; y=b;}

int main(int argc, char* argv[]) { int i1=1; int i2=2; double x=1.5; double y=2.5; A <int> F1; // Создание объекта с использованием шаблона класса A <int> *pF1=&F1; // Указатель на объект параметризированного типа pF1->Fy(i1,i2); std::cout<<pF1->Fx()<<std::endl; A <double> F2; A <double> *pF2=&F2; pF2->Fy(x,y); std::cout<<pF2->Fx()<<std::endl; return 0; }
Пример 12.2. Создание объекта на основе шаблона класса
Закрыть окно





template // Описание шаблона класса

class A

{

T1 x,y;

public:

A();

~A();

T1 Fx();

void Fy(T1 a, T1 b);

};

template A< T1>::A() {} // Реализация методов шаблона класса

template A< T1>::~A() {}

template T1 A< T1>::Fx(){ return x*y;}

template void A< T1>::Fy( T1 a, T1 b) {x=a; y=b;}

int main(int argc, char* argv[])

{ int i1=1;

int i2=2;

double x=1.5;

double y=2.5;

A F1; // Создание объекта с использованием шаблона класса

A *pF1=&F1; // Указатель на объект параметризированного типа

pF1->Fy(i1,i2);

std::cout<Fx()< F2;

A *pF2=&F2;

pF2->Fy(x,y);

std::cout<Fx()<


Работа с памятью


Свободная память, которую можно заказывать в процессе выполнения программы, называется динамической памятью. Для выделения непрерывного блока памяти можно использовать функцию malloc(), а для освобождения - функцию free(). Прототипы этих функций расположены в заголовочном файле stdlib.h.

Например:

int *pIntBuffer; // Выделение памяти под 5 переменных типа int pIntBuffer=malloc(5*sizeof(int));

При отсутствии требуемого количества памяти функция malloc возвращает значение null.

Выделение динамической памяти также можно выполнять оператором new, а освобождение - оператором delete. Оператор new автоматически учитывает тип объекта и выделяет требуемое количество памяти.

Пример:

double *pBuffer; // Выделение памяти под 10 переменных типа double pBuffer=new double [10];


Шаблоны классов


Шаблоны классов позволяют определить параметризированный тип.

Шаблоны классов аналогично шаблонам функций позволяют на этапе компиляции создавать определения классов.

Шаблон класса указывается ключевым словом template, за которым в угловых скобках указывается список типов для создания параметризированного класса. Имя шаблона класса указывается после ключевого слова class.

Для создания объекта на основе шаблона класса после имени шаблона в угловых скобках указывается конкретный тип:

имя_шаблона_класса <тип> имя_объекта.

Например:

template <class T1> // Описание шаблона класса class A { T1 x,y; public: A(); ~A(); T1 Fx(); void Fy(T1 a, T1 b); }; template <class T1> A< T1>::A() {} // Реализация методов шаблона класса template <class T1> A< T1>::~A() {} template <class T1>T1 A< T1>::Fx(){ return x*y;} template <class T1>void A< T1>::Fy(T1 a, T1 b) {x=a; y=b;}

int main(int argc, char* argv[]) { int i1=1; int i2=2; double x=1.5; double y=2.5; A <int> F1; // Создание объекта с использованием шаблона класса A <int> *pF1=&F1; // Указатель на объект параметризированного типа pF1->Fy(i1,i2); std::cout<<pF1->Fx()<<std::endl; A <double> F2; A <double> *pF2=&F2; pF2->Fy(x,y); std::cout<<pF2->Fx()<<std::endl; return 0; }

Пример 12.2. Создание объекта на основе шаблона класса



Шаблоны методов


Шаблоны методов позволяют описывать одинаковую реализацию для различных типов параметров.

Шаблон метода позволяет описать одну реализацию метода для всех допустимых значений параметров. Фактически шаблон метода заменяет набор перегружаемых методов с различными типами параметров.

Шаблон метода - это еще одно проявление полиморфизма языка С++.

Шаблоны методов используют в качестве параметра тип переменной.

В момент компиляции при вызове метода, не имеющего прототипа, выполняется поиск шаблона метода, из которого вызываемый метод может быть сгенерирован.

Шаблон метода указывается ключевым словом template, за которым в угловых скобках указывается список типов, используемых для параметров функции и типа возвращаемого значения.

Например:

template <class T1> T1 Fx (T1 x, T1 y) {return x+y;}

При использовании шаблона компилятор заменит для вызываемого метода тип шаблона на тип, указанный при вызове метода (T1 на int в следующем примере).

Например:

int i1=1; int i2=2; std::cout<<Fx(i1,i2);



Статические члены класса


Переменные и члены класса, объявленные с модификатором доступа static, называются статическими членами класса. Cтатические переменные и методы доступны без создания объекта данного класса. Имя статической переменной квалифицируется именем класса с использованием операции принадлежности :: , а не именем экземпляра класса.

Например:

class A { public: static int iStat; }; int main(int argc, char* argv[]) { A:: iStat = 123; }

Статические методы могут вызывать и использовать только другие статические методы и статические переменные. Из статического метода нельзя выполнять вызов не статического метода - члена класса.

Статическую переменную можно рассматривать как аналог глобальной переменной, которая связана с конкретным классом.

Часто статические переменные называют переменными класса, а не статические переменные - переменными экземпляра.

Для статических переменных могут использоваться указатели.

Например:

class A { public: static int iStatVar; }; int main(int argc, char* argv[]){ int A:: iStatVar = 0; int *piStatVar = &A:: iStatVar; }

При использовании статических переменных указатели всегда являются указателями на существующий тип (в данном примере на тип int), а не на тип класса.

Для того чтобы создать и использовать статическую переменную, следует:

Объявить (как правило, в заголовочном файле) статическую переменную как член класса.

Например: static int iStat;.

Для выделения памяти под эту переменную ее повторно следует объявить в модуле компиляции, в котором она будет использоваться, квалифицируя имя статической переменной именем класса.

Например: int A::iStat;

Выполнить инициализацию статической переменной (по умолчанию статическая переменная типа int имеет значение 0).

Например: A::iStat=123;

Инициализацию статической переменной можно выполнять в теле конструктора в том случае, если при создании объекта с использованием этого конструктора память под статическую переменную уже выделена. Однако инициализировать статическую переменную через конструктор в форме


имя_класса::имя_конструктора : имя_стат_переменной(значение) { }

нельзя ни в каком случае.

Для того чтобы создать статический метод - член класса в среде Visual C++, достаточно выделить в окне Class View секцию с именем класса, в который будет вставлен создаваемый метод, и выполнить команду контекстного меню Add|Add Function. А затем определить тип и имя создаваемого метода, список параметров, и отметить флажок Static, указывающий на то, что создаваемая функция будет статической.

В результате выполненных действий среда Visual C++ добавит в заголовочный файл класса прототип создаваемой статической функции, а в файл реализации - код реализации создаваемой статической функции.

Например:

class A {public: a1(void); ~a1(void); int Fx1(int i1); int Fx2(int i2); protected: static int Fstat1(void); // Статическая функция доступна //только для статических методов - членов //данного класса и наследуемых классов public: static int Fstat2(void); // Общедоступная статическая функция };

int main(int argc, char* argv[]){ std::cout<<a1::Fstat2(); // Вызов статической функции }

Реализация статической функции записывается так же, как и реализация любого другого метода - члена класса. При этом ключевое слово static не указывается.

Например:

int A::FStat1(void) { return 0; }


Виртуальные классы


Для того чтобы при множественном наследовании один и тот же базовый класс не порождал для объекта производного класса несколько объектов базового класса, при объявлении производного класса такой базовый класс указывается с ключевым словом virtual и называется виртуальным классом.

Например:

class A : virtual public B { }



Виртуальные методы


Виртуальные методы объявляются в базовом классе с ключевым словом virtual, а в производном классе могут быть переопределены. Прототипы виртуальных методов как в базовом, так и в производном классе должны быть одинаковы.

Применение виртуальных методов позволяет реализовывать механизм позднего связывания, при котором определение вызываемого метода происходит на этапе выполнения, а не на этапе компиляции. При этом вызываемый виртуальный метод зависит от типа объекта, для которого он вызывается. При раннем связывании, используемом для не виртуальных методов, определение вызываемого метода происходит на этапе компиляции.

На этапе компиляции строится таблица виртуальных методов, а конкретный адрес проставляется уже на этапе выполнения.

При вызове метода с использованием указателя на класс действуют следующие правила:

для виртуального метода вызывается метод, соответствующий типу объекта, на который указывает указатель.для не виртуального метода вызывается метод, соответствующий типу самого указателя.

В следующем примере иллюстрируется вызов виртуальных методов:

class A // Объявление базового класса { public: virtual void VirtMetod1(); // Виртуальный метод void Metod2(); // Не виртуальный метод }; void A::VirtMetod() { cout << "Вызван A::VirtMetod1\n";}

void A::Metod2() { cout << "Вызван A::Metod2\n"; }

class B : public A // Объявление производного класса {public: void VirtMetod1(); // Виртуальный метод void Metod2(); // Не виртуальный метод }; void B::VirtMetod1() { cout << "B::VirtMetod1\n";} void B::Metod2() { cout << "B::Metod2\n"; } void main() { B aB; // Объект класса B B *pB = &aB; // Указатель на объект класса B A *pA = &aB; // Указатель на объект класса A pA->VirtMetod1(); // Вызов метода VirtMetod класса B pB->VirtMetod1(); // Вызов метода VirtMetod класса B pA->Metod2(); // Вызов метода Metod2 класса A pB->Metod2(); // Вызов метода Metod2 класса B }

Пример 12.1. Вызов виртуальных методов

Результатом выполнения этой программы будут следующие строки:

Вызван B::VirtMetod1 Вызван B::VirtMetod1 Вызван A::Metod2 Вызван B::Metod2

Чисто виртуальной функцией называется виртуальная функция, указанная с инициализатором

=0.

Например:

virtual void F1( int) =0;

Объявление класса может содержать виртуальный деструктор, используемый для удаления объекта определенного типа. Однако виртуального конструктора в языке С++ не существует. Некоторой альтернативой, позволяющей создавать объекты заданного типа, могут служить виртуальные методы, в которых выполняется вызов конструктора для создания объекта данного класса.

Например:

class A{ public: A(); A (const A&); virtual A* virt_object1 () { return new A(); } virtual A* virt_object2 () { return new A(*this); } }



Абстрактные классы


Абстрактные классы содержат набор методов, который должен быть реализован в производном классе. Абстрактные классы и абстрактные методы помечаются модификатором abstract. Если в подклассе абстрактного суперкласса не выполнена реализация всех абстрактных методов, то подкласс по умолчанию также является абстрактным.



Эквивалентность объектов


Для определения равенства различных объектов применяется метод equals.

Метод equals реализован в классе Object и соответственно наследуем любым классом Java. Большинство классов переопределяет этот метод таким образом, что позволяет сравнивать на эквивалентность объекты конкретного класса.

Например:

class A { String objectName; A (String name) { objectName = name; } // Конструктор } public class MyA { public static void main (String args[ ]) { A A = new A("Class1"); // Создание экземпляра класса

A A_eq = A; // Ссылка на существующий объект

A A_clon = (A)A.clone; // Создание объекта методом clone

A A_2 = new A("Class2"); // Сравнение объектов: if (A.equals(A_eq)) { } if (A.equals(A_ clon)) { } if (A.equals(A_ 2)) { } } }

Выполнив приведенный пример, можно увидеть, что эквивалентными будут только экземпляр класса и объект, созданный как ссылка на данный экземпляр класса. А экземпляр класса и его клон, так же, как и различные экземпляры одного класса, не совпадают.

Принадлежность объекта к конкретному классу проверяется с помощью оператора instanseof.



Механизмы наследования


Класс - это шаблон, в котором определяются данные и поведение объекта. Объекты одного класса совместно используют общую структуру данных и общее поведение.

Объявление класса в языке Java создает новый ссылочный тип, определяющий как описание методов, так и их реализацию.

Объявление интерфейса создает новый ссылочный тип, который специфицирует описание методов и имена некоторых констант, но не определяет саму их реализацию.

Интерфейс может быть объявлен для расширения одного или нескольких интерфейсов.

Наследование позволяет определять новые классы в терминах существующих классов.

В языке Java поддерживается только простое наследование: любой подкласс является производным только от одного непосредственного суперкласса. При этом любой класс может наследоваться от нескольких интерфейсов.

Наследование интерфейсов реализует некоторую замену множественному наследованию, когда вместо того чтобы один класс имел несколько непосредственных суперклассов, этот класс наследует несколько интерфейсов. Интерфейс позволяет определить некоторый шаблон класса: описание методов без их реализации.

Язык Java разрешает несколько уровней наследования, определяемых непосредственным суперклассом и косвенными суперклассами. Наследование можно использовать для создания иерархии классов.

При создании подкласса на основе одного или нескольких суперклассов возможны следующие способы изменения поведения и структуры класса:

расширение суперкласса путем добавления новых данных и методов;замена методов суперкласса путем их переопределения;слияние методов из суперклассов вызовом одноименных методов из соответствующих суперклассов.



Объявление интерфейса


Объявление интерфейса вводит новый ссылочный тип, членами которого являются константы и абстрактные методы.

Реализация интерфейса всегда выполняется в классе, который использует данный интерфейс.

Один класс может реализовывать несколько интерфейсов, и один интерфейс может использоваться несколькими классами.

Интерфейсы позволяют разделять общую структуру - методы и константы, классами, не связанными между собой иерархией наследования.

Объявление интерфейса может иметь следующее формальное описание:

МодификаторИнтерфейса ИдентификаторИнтерфейса extends СписокНаследуемыхИнтерфейсов {ТелоИнтерфейса}

Самым простым примером объявления интерфейса может быть следующий код:

interface MyColors { int RED = 1, YELLOW = 2, BLUE = 4; }

Интерфейс может иметь в качестве предков только интерфейсы.

Интерфейс может иметь модификаторы public и abstract. Язык Java подразумевает, что каждый интерфейс по умолчанию имеет модификатор abstract, который не рекомендуется указывать явно.

К интерфейсу, объявленному с модификатором доступа public, может быть произведен доступ из других пакетов, в противном случае - только из своего пакета.

Тело интерфейса заключается в фигурные скобки и не может содержать конструктора или блоков инициализации статических переменных. В остальном тело конструктора идентично телу абстрактного класса.

Любая переменная интерфейса по умолчанию считается переменной с модификаторами public, static и final и обязательно должна быть инициализирована константным выражением. Также каждая переменная в теле интерфейса неявно считается переменной с модификатором.

Метод, объявленный в теле интерфейса, по умолчанию считается методом с модификаторами abstract и public. Объявление метода завершается точкой с запятой и не содержит тела метода, заключенного в фигурные скобки.

Метод интерфейса не может иметь модификаторов final или static.

Интерфейс можно использовать как ссылочный тип при объявлении переменных. Переменная или выражение типа интерфейса могут ссылаться на любой объект, который является экземпляром класса, реализующего данный интерфейс. Переменную типа интерфейса можно использовать только после присвоения ей ссылки на объект ссылочного типа, для которого был реализован данный интерфейс.



Объявление класса


Объявление класса вводит новый ссылочный тип и определяет или часть или всю его реализацию.

При загрузке класса выделяется память для всех его статических переменных и затем выполняется их инициализация.

Объявление класса может иметь следующее формальное описание:

МодификаторыКласса class ИмяКласса extends ИмяСуперКласса implements СписокРеализуемыхИнтерфейсов {ТелоКласса}

Тело класса содержит описание переменных, методов и вложенных классов и заключается в фигурные скобки. В частном случае тело класса может не содержать ни одного объявления.

Например:

public class A implements B { public A() { } public int Metod1(){return 0;} } interface B { public int Metod1(); }

Вложенный класс описывается так же, как и внешний.

В описании каждого класса указывается имя класса и тело класса, содержащее объявления полей класса. Дополнительно для класса могут быть заданы модификаторы класса и указан непосредственный суперкласс и реализуемые классом интерфейсы. Полями класса в Java называются переменные и методы, определяемые в теле класса.

Класс может иметь более одного модификатора класса.

В языке программирования Java существуют следующие модификаторы класса:

abstract, являющийся модификатором реализации класса и определяющий, что класс может иметь абстрактные методы (методы, не имеющие реализации);final, являющийся модификатором ограничения иерархии классов и указывающий, что класс не может иметь подклассов (не должен никогда появляться во фразе extends объявления класса). Класс с модификатором final обязан реализовать все свои интерфейсы.public является модификатором доступа и указывает, что к данному классу разрешен доступ из других пакетов. Доступ может быть выполнен или непосредственно указанием квалифицированного имени класса, или с использованием оператора import.

Для указания модификаторов класса применяются следующие правила:

если класс, имеющий абстрактные методы, объявлен без модификатора abstract, то возникает ошибка компиляции;для абстрактных классов нельзя создавать экземпляры класса;если при объявлении класса не указан модификатор public, то класс доступен только внутри своего пакета;при компиляции каждый общедоступный класс всегда записывается в отдельный файл с расширением .CLASS.
Такой файл называется модулем компиляции; имя общедоступного класса должно совпадать с именем файла, содержащего код класса на языке Java;один модуль компиляции может содержать только один класс или интерфейс, имеющие модификатор public;один модуль компиляции может одновременно содержать объявление общедоступного класса и нескольких интерфейсов или классов, не имеющих модификатора public; один модуль компиляции не может одновременно содержать объявление общедоступного класса (public class) и общедоступного интерфейса (public interface);объявление класса одновременно с модификаторами final и abstract вызывает ошибку компиляции.

Любой класс может иметь только один суперкласс, указываемый ключевым словом extends. Наследуемый суперкласс должен быть доступным классом и не иметь модификатора final.

По умолчанию предполагается, что в том случае, если для класса не указано никакого суперкласса, то его непосредственным суперклассом станет класс Object из пакета java.lang, который всегда является корневым классом в любой формируемой иерархии классов.


Пакеты классов JDK


Инструментальный набор JDK включает утилиты и пакеты классов.

Собственно говоря, все стандартные возможности программирования на Java базируются на классах этого инструментального набора. JDK состоит из нескольких отдельных пакетов, иногда называемых библиотеками. Различные среды программирования, такие как JBuilder, предоставляют дополнительные пакеты классов.

Среда JBuilder 8 Enterprise полностью поддерживает работу со всеми классами пакета JDK 1.4.


Приведение ссылочных типов


В языке Java преобразование ссылочных типов может происходить в четырех контекстах:

приведение;присваивание;вызов метода;выполнение арифметических действий.

Наиболее общим случаем является приведение.

В языке Java объявление класса определяет одноименный ссылочный тип. Все переменные, имеющие ссылочный тип, указываемый именем класса, являются объектами. Тип такого объекта определяется оператором instanceof.

Например:

String str1; Object ObjectName; ObjectName =(Object) str1; // Приведение типа

if (ObjectName instanceof String) { // Тип объекта String String str2 = (String) ObjectName ; System.out.println(str2); // Запись в стандартный поток // вывода значения строки, // указанной параметром str2 }

В рассмотренном примере тип объекта str1 сначала приводится к типу своего косвенного суперкласса Object, а затем оператором instanceof определяется непосредственный тип объекта (при определении оператором instanceof типа объекта ObjectName как String или Object возвращается значение true).

При приведении ссылочных типов действуют следующие правила:

объект всегда может быть приведен к типу своего непосредственного суперкласса;приведение ссылочных типов может выполняться по иерархии классов сколь угодно глубоко;любой класс ссылочного типа всегда можно привести к типу Object.

После приведения объекта к типу суперкласса все переменные и методы самого класса объекта становятся недоступными для приведенного объекта.

Преобразование присвоением происходит в том случае, когда применяясь к переменной и значению, преобразует значение к типу переменной. При преобразовании присвоением не требуется проверки времени выполнения.

Значение простого типа не может быть присвоено значению ссылочного типа, как и значение ссылочного типа не может быть присвоено значению простого типа.

Присвоение значений ссылочного типа переменным ссылочного типа не требует во время выполнения действий преобразования. Просто компилятор должен иметь возможность преобразовать тип значения времени компиляции к типу переменной.


Например:

public class A { int x, y; } public class ADerived extends A { int z; } public interface B { void setColor(int color); } public class AB extends A implements B { int color; public void setColor(int color) { this.color = color; } } class MyA{ public static void main(String[ ] args) { // Создание переменных ссылочного типа // и присваивание им значения: A p = new A(); p = new ADerived(); // Правильно: ADerived это // подкласс класса A ADerived p3d = p; // Ошибка

// Присваивание значения переменной // типа Object: Object o = p; // Правильно: любой объект приводим // к типу Object int[ ] a = new int[3]; Object o2 = a; // Правильно: массив приводим к Object

// Присваивание значения переменной // типа интерфейса: AB cp = new AB(); B c = cp; // Правильно: AB реализует интерфейс B

// Присваивание значения переменной // типа массива: byte[ ] b = new byte[4]; a = b; // Ошибка: это массивы различных типов ADerived[ ] p3da = new ADerived[3]; A[ ] pa = p3da; // Правильно: ADerived подкласс класса A p3da = pa; // Ошибка, т.к. необходимо приведение } }

Преобразование приведением является более общим: если преобразование возможно, то приведение может его выполнить. Часто следует явно указывать преобразование приведением, чтобы компилятор имел строгую информацию о типе объекта. Для любого типа значения времени компиляции всегда можно выполнить приведение к тому же самому типу. Это позволит точно указать, что результирующее значение будет иметь именно указанный тип.

Приведением можно преобразовывать значения одного вычислимого типа к другому. Тип boolean не может быть приведен ни к какому другому типу.

Значения простых типов не могут быть приведены к значениям ссылочных типов и наоборот.


Раннее и позднее связывание


Раннее и позднее связывание является одним из проявлений полиморфизма, позволяя выполнять одним оператором вызов различных методов в зависимости от типа объекта.

В следующем примере оператор b.MyPrint() будет вызывать различные методы в зависимости от типа объекта, для которого он выполняется:

class B{ void MyPrint () { }} class B1 extends B{ void MyPrint () { //Переопределение метода } } class B2 extends B{ void MyPrint () { //Переопределение метода } } class B3 extends B{ void MyPrint () { //Переопределение метода } } class PrintB{ B DoBObject () { // Метод, реализующий класс B B b; b= new B1(); … b= new B2(); … b= new B3(); … return b; } ... B b= new PrintB.DoBObject(); b.MyPrint() // Выполняет вызов метода MyPrint в // соответствии с типом объекта b



Вложенные классы


Проблемы, возникающие из-за отсутствия множественного наследования, частично можно решить с использованием вложенных классов.

Если требуется, чтобы класс ClassA наследовал все доступные методы, включая и protected-методы, двух классов (класса ClassB и класса ClassC), то реализовать такую схему можно через вложенный класс.

Класс ClassA создается производным от класса ClassB. Это позволит всем экземплярам класса ClassA вызывать protected-методы класса ClassB.

В классе ClassA объявляется подкласс ClassFromC, производный от класса ClassC и переопределяющий все protected-методы класса ClassC, реализуя их как вызовы соответствующих методов из суперкласса.

При такой реализации экземпляр класса ClassA получает доступ к protected-методам двух различных классов - своего суперкласса и своего вложенного класса.



Конструкторы


Конструктором называется метод, вызываемый для инициализации объекта при его создании.

Имя конструктора всегда совпадает с именем класса. Конструктор не может использовать оператор return и для него не указывается никакого типа возвращаемого значения. При объявлении конструктора можно указать модификаторы доступа public, protected или private.

Наличие явно описанного конструктора в классе не является обязательным. В этом случае при создании объекта используется конструктор по умолчанию. Такой конструктор не получает параметров и вызывает непосредственно конструктор суперкласса: super().

При этом, если суперкласс не имеет конструктора без списка параметров, то происходит ошибка компиляции.

Конструкторы не наследуются подклассами.

Тело конструктора заключается в фигурные скобки.

При этом первыми операторами должны быть указаны, если они используются, операторы вызова конструкторов данного класса или суперкласса.

Конструктор может иметь следующее формальное описание:

ИмяКласса (СписокПараметров) { ВызовКонструкторов БлокОператоров }

Вызов конструкторов выполняется операторами this и super с указанием в скобках списка параметров.

Например:

public class C extends A { public C() {this(1,20); } // Вызов конструктора данного класса public C(int i, int j) { } }

Вызов конструкторов подчиняется следующим правилам:

при создании объекта любого заданного класса будет неявно выполнена цепочка вызовов всех конструкторов его суперклассов;первым будет выполнено тело конструктора для Object;каждый последующий конструктор в цепочке будет выполняться только после выполнения конструктора своего непосредственного суперкласса;при создании объекта будут инициализированы все переменные экземпляра.



Объявление переменных


В языке Java при объявлении переменной - указании ее типа и имени - одновременно можно выполнить ее инициализацию.

Для переменной могут быть указаны следующие модификаторы доступа:

public или protected, или private, - определяющие область видимости переменной;static - указывает, что переменная является переменной класса, а не переменной экземпляра класса;final - запрещает присвоение значений переменной вне тела класса, в котором она объявлена;transient - указывает, что переменная не является частью постоянного состояния объекта;volatile - определяет возможность асинхронного изменения переменной.

Переменная, не имеющая модификатора static, называется переменной экземпляра. Переменная экземпляра создается для каждого нового экземпляра класса.

Переменная, имеющая модификатор static, называется переменной класса. Для всех экземпляров класса существует только одна переменная класса. Эта переменная доступна и до создания экземпляра класса.

Переменная, имеющая модификатор final, должна быть инициализирована внутри тела класса, так как вне тела класса final-переменная является константой и ее изменение или инициализация запрещены.

Модификатор transient нельзя использовать совместно с модификаторами final или static.

Любая переменная может иметь модификатор доступа public (общедоступный), protected (защищенный) или private (скрытый).

Для этих модификаторов определены следующие правила доступа:

public указывает, что данная переменная будет доступна везде, где доступен класс, в котором она объявлена;protected указывает, что данная переменная будет доступна как внутри пакета, содержащего объявление класса, в котором она определена, так и внутри любого подкласса данного класса;private указывает, что данная переменная не будет доступна нигде кроме тела класса, в котором она объявлена.

По умолчанию переменная считается доступной везде внутри пакета, содержащего класс, в котором она объявлена, и не недоступна ни в каком другом подклассе указанного класса в том случае, если подкласс содержится в другом пакете.

Инициализация переменной означает присвоение этой переменной некоторого значения. В качестве значения инициализации могут выступать:

выражение;массив, указываемый в фигурных скобках.

Если переменная объявлена с модификатором static, то инициализация выполнятся сразу при загрузке класса.

Если объявляемая переменная является переменной экземпляра, то инициализация выполняется при выполнении метода-конструктора класса.

Если объявляемая переменная является локальной переменной, то ее инициализация происходит при выполнении оператора объявления этой переменной.

Например:

int i1,i2; float f1 = 3.0; float dArray1 [] = new float [14]; java.lang.String str1 = "no?iea"; Object oObj = str1 ; Exception e = new Exception ( ) ;



Определение методов


В языке Java определение метода включает его объявление и реализацию. Определение метода всегда указывается в теле класса.

Метод может иметь модификаторы доступа, возвращать значение и получать параметры.

Метод может иметь следующие модификаторы:

public, protected или private -модификаторы доступа;static - модификатор метода класса.abstract, final, native или synchronized.

Для модификаторов доступа метода определены следующие правила:

public указывает, что данный метод будет доступен везде, где доступен класс, в котором он определен;protected указывает, что данный метод будет доступен как внутри пакета, содержащего объявление класса, в котором он определен, так и внутри любого подкласса данного класса; private указывает, что данный метод не будет доступен нигде кроме тела класса, в котором он определен.

По умолчанию метод считается доступным везде внутри пакета, содержащего класс, в котором он определен, и недоступным ни в каком другом подклассе указанного класса в том случае, если подкласс содержится в другом пакете.

Метод, не имеющий модификатора static, называется методом экземпляра. Метод экземпляра может быть вызван только для созданного экземпляра класса или подкласса. Такой метод нельзя вызывать непосредственно, квалифицируя его именем класса.

Метод, объявленный с модификатором static, называется статическим методом (или методом класса) и может быть вызван без создания экземпляра класса. Этот метод всегда вызывается непосредственно из класса. Статический метод имеет доступ к другим статическим переменным и методам данного класса.

Если статический метод определен как final-метод, то он не может быть переопределен.

Например:

// Файл A.java package classa; public class A implements B { public A() { } static int b=1; public int Metod1(){return a;} public static int Metod2(){ return 0;} //Статический метод } interface B { final public static int a=1; // Статическая переменная int Metod1(); }

// Файл C.java package classa; public class C extends A { public C() { } static int b=3; public int Metod1(){return a;} public static int Metod2(){return 77;} public static void main(String[] args) { System.out.println(A.Metod2()); System.out.println(C.Metod2()); } }


При переопределении методов их модификаторы доступа должны совпадать. Так, нельзя переопределить метод, имеющий модификатор доступа public, методом с модификатором доступа private.

Абстрактный метод указывается модификатором abstract. Такой метод никогда не имеет тела метода: вместо фигурных скобок, ограничивающих тело метода, объявление метода завершается точкой с запятой.

Абстрактные методы можно объявлять только в абстрактных классах или интерфейсах. Объявление абстрактного метода в классе, не имеющем модификатора abstract, приводит к ошибке компиляции. Любой подкласс абстрактного класса, который сам не является абстрактным классом, должен определять реализацию всех наследуемых не реализованных абстрактных методов.

Например:

public class A extends AA implements B { public A() { } public int Metod1(){return 0;} public static int Metod2(){return 0;} int Metod3(){return 0;} public int Metod4(){return 0;} } interface B { int Metod1(); abstract int Metod4(); } abstract class AA{ abstract int Metod3(); }

Методы, объявленные с модификатором private, не могут быть абстрактными методами, так как они недоступны вне тела класса. Статические методы также не могут выступать в качестве абстрактных методов, так как считаются конечными и не могут быть переопределены.

Объявление метода с модификатором final запрещает его последующее переопределение. Такие методы называются конечными методами, и по умолчанию считается, что private-метод всегда является конечным методом.

Методы, объявленные с модификатором native, могут иметь реализацию на другом языке программирования. Эти методы используются для написания машинно-зависимого кода. native-методы в Java-программе не имеют тела метода.

Synchronized-методы выполняются с блокировкой:

для метода класса выполняется блокировка класса,для метода экземпляра - блокировка объекта.

Это позволяет предотвратить параллельный доступ к данным из различных потоков в многопоточном приложении.

Метод может возвращать значение заданного типа. В этом случае:

перед именем метода указывается тип возвращаемого значения;в теле метода присутствует оператор return, определяющий возвращаемое значение.

Если метод не имеет возвращаемого значения, то он должен быть объявлен с ключевым словом void, указывающим на отсутствие возвращаемого значения.

Если в качестве возвращаемого значения должен быть использован массив, то после имени метода может быть указана пара квадратных скобок или список параметров и пара квадратных скобок.


Передача параметров


Список формальных параметров содержит упорядоченные и разделенные запятой описания параметров (тип параметра и идентификатор параметра). Если метод не имеет параметров, передаваемых ему при вызове, то после имени метода указывается пара пустых скобок.

В языке Java все параметры передаются по значению. При передаче параметра ссылочного типа его значением является ссылка на объект, поэтому в метод передается ссылка на экземпляр класса.

Параметры в языке Java рассматриваются как локальные переменные метода, специфицированные вне тела метода. При вызове метода перед его выполнением происходит вычисление выражений, представляющих фактические передаваемые методу параметры, и присвоение вычисленных значений соответствующим локальным переменным (формальным параметрам).



Перегружаемые методы


Перегружаемыми методами называется группа методов с одинаковым именем, но различными списками параметров. Язык Java позволяет определять в классах перегружаемые методы.



Переопределение метода


Переопределением метода называется объявление в подклассе метода, у которого имя, тип и список параметров, в точности совпадают с соответствующими атрибутами метода, ранее объявленного в суперклассе.

При переопределении метода использование модификаторов доступа должно удовлетворять следующим условиям:

если переопределяемый метод суперкласса не содержит ни одного из модификаторов доступа public, protected или private, то переопределяющий метод не должен иметь модификатора private.если переопределяемый метод суперкласса имеет модификатор доступа protected, то переопределяющий метод должен иметь модификатор доступа public или protected.если переопределяемый метод суперкласса имеет модификатор доступа public, то переопределяющий метод должен также иметь модификатор доступа public.

Тип возвращаемого значения переопределяющего метода должен совпадать или преобразовываться присвоением к типу возвращаемого значения переопределяемого метода.

Если класс содержит переопределяющий метод, то переопределенный метод можно вызвать только с квалификацией ссылки ключевым словом super или именем суперкласса (для статических членов).

Например:

class A { public void Metod1() { } public static void Metod2() { }

} class B extends A { public Metod1() { } ... Metod1(); // Вызов метода из класса B super.Metod1(); // Вызов метода из класса A A.Metod2(); }



Создание объекта


При создании объекта последовательно выполняются следующие действия: вызывается конструктор, создается новый объект указанного типа, и все переменные экземпляра инициализируются своими значениями по умолчанию, затем формируется ссылка на созданный и инициализированный объект. Эта ссылка и является значением выражения, создающего объект.

Объект может быть создан вызовом метода newInstance. В этом случае используется конструктор без списка параметров.



Ссылка на текущий объект


При вызове метода экземпляра ключевое слово this

указывает ссылку на текущий объект. Это может быть использовано для передачи самого объекта в качестве аргумента методу самого экземпляра класса;используется для указания квалифицированной ссылки на поля (переменные и методы) экземпляра класса.

Ключевое слово super используется для указания квалифицированной ссылки на поля (переменные и методы) суперкласса.

По умолчанию любой метод первоначально ссылается на свои собственные переменные и методы и только в случае их отсутствия выполняется поиск этих полей вверх по иерархии суперклассов. При этом, если метод не переопределяется, то его имя не обязательно квалифицировать.

Например:

class A { public int a; } class B extends A { public int a, b; public void Metod1 ( ) { b=a; // Ссылка на this.a b=super.a; } }

Имя суперкласса может быть использовано для квалифицированного доступа к статическим переменным или методам.



Статические переменные


Для каждого класса создается только один экземпляр статической переменной (переменой класса).

При описании статических переменных должны быть выполнены следующие правила:

статические переменные должны быть объявлены вне всякого метода;статические переменные могут быть инициализированы при их объявлении или блоком кода, отмеченного ключевым словом static, и также расположенным вне метода.

Инициализация статических переменных выполняется при загрузке класса. Инициализация переменных выполняется в порядке их следования в объявлении класса.

Например:

public class C { public C() { } static int b=3; static int c; static { b++; c = 10; } // Инициализация статической переменной }

При инициализации статических переменных можно вызывать статические методы.