- •1 Перегрузка функций
- •Шаблоны функций
- •Void main()
- •3 Класс — как расширенное понятие структуры
- •4 Конструкторы, деструкторы и доступность компонентов класса
- •Void main ( )
- •5 Компонентные данные
- •7 Указатель this
- •Void main()
- •8 Друзья классов
- •9 Перегрузка стандартных операций
- •Void main ()
- •10 Простое наследование
- •11 Виртуальные функции и абстрактные классы
- •12 Абстрактные классы
- •13 Множественное наследование
- •Void main()
Void main()
{ int n=4;
int X[ ]={10,20,30,14};
cout << “\n rmax(n,X) = “ << rmax(n,X);
rmax(n,X)=0;
for ( int i=0; i<n; i++)
cout << “\t X[“<<i<<“] =“ <<X[i];
float arx[ ]={10.3,20.4,10.5};
cout << “\n rmax(3,arx) = “ << rmax(3,arx);
rmax(3,arx)=0;
for (i=0; i<3; i++)
cout << “\t arx[“<<i<<“] =“ <<arx[i];}
Результат
rmax(n,x)=30 X[0]=10 X[1]=20 X[2]=0 X[3]=14
rmax(3,arx)=20.4 arx[0]=10.3 arx[1]=0 arx[2]=10.5
Основные свойства параметров шаблона:
- имена шаблона должны быть уникальны во всем определении шаблона;
- список параметров шаблона не может быть пустым;
- в списке параметров шаблона функции может быть несколько параметров. Каждый из них должен начинаться служебным словом class;
- недопустимо использовать в заголовке шаблона параметры с одинаковыми именами;
- имя параметра шаблона имеет в определяемой шаблоном функции все права имени типа.
2
Из основных типов языка С++ пользователь может конструировать производные типы, например структуры и объединения. Структуры и объединения отнесены к структурированным типам.
Структура как составной тип данных
Структура - это объединенное в единое целое множество поименованных элементов в общем случае разных типов. Пусть, библиотечная карточка каталога должна включать сведения, которые приведены для книг в списке литературы. Таким образом, для каждой книги будет указываться следующая информация: фамилия и инициалы автора;
заглавие книги; количество страниц.
Если к библиотечной карточке каталога нужно обращаться как к единому целому, то воспользоваться массивом для представления всех ее данных весьма сложно. Все данные имеют разные длины и разные типы. Объединить такие разнородные данные удобно с помощью структуры. Каждая структура включает в себя один или несколько объектов (переменных, массивов, указателей, структур и т. д.), называемых элементами структуры. Сведения о данных, входящих в библиотечную карточку, с помощью структуры можно представить таким структурным типом:
struct card {char *author; // Ф.И.О. автора книги
char *title; // Заголовок книги
int pages; // Количество страниц };
Такое определение вводит новый производный тип, который называется структурным типом. В данном примере у этого структурного типа есть конкретное имя card .
В соответствии с синтаксисом языка определение структурного типа начинается со служебного слова struct, вслед за которым помещается выбранное пользователем имя типа. Описание элементов, входящих в структуру, помещаются в фигурные скобки, вслед за которыми ставится точка с запятой. Элементы структуры могут быть как базовых, так и производных типов. Например, в структурах типа card будут элементы базового типа int и производного типа char * .Определив структурный тип, можно определять и описывать конкретные структуры, т. е. структурированные объекты, например, так : сard rec1, rec2;
Здесь определены три структуры (три объекта) с именами rec1, rec2,. Каждая из этих структур содержит в качестве элементов свои собственные данные: char *title;
char *city; состав которых определяет структурный тип с именем card.
Если структура определяется однократно, т.е. нет необходимости в разных частях программы определять или описывать одинаковые по внутреннему составу структурированные объекты, то можно не вводить именованный структурный тип, а непосредственно определять структуры одновременно с определением их компонентного состава. Следующий оператор определяет две структуры с именами XX, YY, массив структур с именем ЕЕ и указатель pst на структуру: struct { char N[12]; int value; } XX, YY, EE[8], *pst;
В ХХ, YY и в каждый элемент массива EE[0],...,EE[7] входят в качестве элементов массив char N[12] и целая переменная value. Имени у соответствующего структурного типа нет.
Для обращения к объектам, входящим в качестве элементов в конкретную структуру, чаще всего используются уточненные имена. Общей формой уточненного имени элемента структуры является следующая конструкция: имя_структуры.имя_элемента_структуры
Например, для определенной выше структуры YY оператор YY.value = 86;
присвоит переменной value значение 86 .
При определении структур возможна их инициализация, т.е. задание начальных значений их элементов. Например, введя структурный тип card, можно следующим образом определить и инициализировать конкретную структуру:
card dictionary= {“ Hornby A.S.”, ”Oxford students\
dictionary of Current English”, “Oxford”, “Oxford University”, 1994, 769 };
С именем структурного типа не связан никакой конкретный объект, и поэтому с его помощью нельзя сформировать уточненные имена элементов. Определение структурного типа вводит только шаблон (формат, внутреннее строение) структур. Идентификатор card в примере - это название структурного типа (т.е. “ярлык” или “этикетка” структур, которые будут определены в программе).Определение структурного типа может быть совмещено с определением конкретных структур этого типа: struct PRIM {char *name; long sum; } A, B, C;
Здесь определен структурный тип с именем PRIM и три структуры A, B, C, имеющие одинаковое внутреннее строение. Так как имя структурного типа обладает всеми правами имен типов, то разрешено определять указатели на структуры:
имя_структурного_типа *имя_указателя_на_структуру;
Как обычно, определяемый указатель может быть инициализирован. Значением каждого указателя на структуру может быть адрес структуры того же типа, т. е., грубо говоря, номер байта, начиная с которого структура размещается в памяти. Структурный тип задает ее размеры и тем самым определяет, на какую величину (на сколько байтов) изменится значение указателя на структуру, если к нему прибавить 1. Например, после определений структурного типа card и структуры rec2 можно так записать определение указателя на структуру типа card: card *ptrcard = &rec2;Здесь определен указатель ptrcard и ему с помощью инициализации присвоено значение адреса одной из конкретных структур типа card. После определения указателя появляется еще одна возможность доступа к элементам структуры rec2. Ее обеспечивает операция ‘->‘ доступа к элементу структуры, с которой в этот момент связан указатель. Формат выражения таков:имя_указателя -> имя_элемента_структуры
ptrcard -> pages Вторая возможность обращения к элементу структуры с помощью адресующего ее указателя - это разыменование указателя и формирование уточненного имени такого вида: (*имя_указателя) .имя_элемента_структуры
Т.е. следующие три выражения эквивалентны: (*ptrcard) .pages
ptrcard -> pages rec2 .pages
они имеют один и тот же элемент int pages конкретной структуры rec2, имеющей тип card. Как и для других объектов, для структур могут быть определены ссылки:
имя_структурного_типа& имя_ссылки_на_структуру_инициализатор; Например, для введенного выше структурного типа PRIM можно таким образом ввести ссылки на структуры A, B: PRIM& refA = A; PRIM& refB (B);
После таких определений refA есть синоним имени структуры А, refB есть другое имя для структуры В. Теперь возможны, например, такие обращения: A .sum эквивалентно refA.sum;
*B .name эквивалентно *refB .name; A .name эквивалентно refA.name.
Спроектируем простейшую базу данных, построив ее в виде двухсвязного списка структур (рис. 4), каждая из которых имеет такой формат:
struct record { card book; // Структура с данными о книге
record *prior; // На предыдущий элемент списка
record *next; // На следующий элемент списка };
Структурный тип record предусматривает, что в каждую структуру входят три элемента: структура типа card и два указателя на структуры типа record. Предполагается, что до определения структурного типа record уже определен структурный тип card.
Указатель на структуру может входить в определение того же структурного типа. Именно так в определении формата структуры record введены два указателя: record *prior - указатель на предыдущий элемент в двухсвязном списке структур; record *next - указатель на следующий элемент. При определении с помощью typedef имени для структурного типа, у последнего может отсутствовать основное имя. Например, структурный тип можно ввести и следующим образом: typedef struct { char *name; long sum; } STRUCT; STRUCT struct_st , *struct_ps , struct_as[8];
В данном примере имя типа STRUCT вводится с помощью typedef для структуры, тип которой не поименован. Затем имя типа STRUCT используется для определения объектов. Таким образом, имеются две возможности определения имени структурного типа.
В отношении элементов структур существует практически только одно существенное ограничение - элемент структуры не может иметь тот же самый тип, что и определяемый структурный тип. Таким образом, следующее определение структурного типа ошибочно:
struct mistake { mistake s; int m; }; // Ошибка!
В то же время элементом определяемой структуры может быть указатель на структуру определяемого типа: struct correct {correct *ps; long f; }; // Правильно!
Элементом определяемой структуры может быть структура, тип которой уже определен: struct begin { int k; char *h; } strbeg; struct next {begin beg; float d; };
Если в определении структурного типа нужно в качестве элемента использовать указатель на структуру другого типа, то разрешена такая последовательность определений:
struct A; struct B { struct A *pa; }; struct A{ struct B *pb; };
Неполное определение структурного типа А можно использовать в определении структурного типа В, так как определение указателя pa на структуру типа А не требует сведений о размере структуры типа А. Последующее определение в той же программе структурного типа А обязательно. Функция может возвращать структуру как результат:
struct help { char *name; int number; }; help func1 (void) ; // Прототип функции
Функция может возвращать указатель на структуру:
help *func2 (void) ; // Прототип функции
Функция может возвращать ссылку на структуру:
help& func3 (void) ; // Прототип функции
Через аппарат параметров информация о структуре может передаваться в функцию либо непосредственно, либо через указатель, либо с помощью ссылки:
void func4 (help str) ; // Прямое использование
void func5 (help *pst); // С помощью указателя
void func6 (help& rst); // С помощью ссылки
Рассмотрим применение операции new к структурам. Операндом для операции new может быть структурный тип. В этом случае выделяется память для структуры использованного типа, и операция new возвращает указатель на выделенную память. Память может быть выделена и для массива структур, например, так:last = new record[9]; // Память для массива структур В этом случае операция new возвращает указатель на начало массива. Дальнейшие действия с элементами массива структур подчиняются правилам индексации и доступа к элементам структур. Например, разрешен такой оператор: last[0] .next = NULL;
Объединение разнотипных элементов
Объединения вводятся с помощью служебного слова union. Пусть дана структура
struct { long L; int i1, i2; char c[4]; } STR;
Каждый элемент структуры имеет свое место в памяти, и размещаются эти элементы последовательно. Определим очень похожее внешне на структуру STR объединение UNI:
union { long L ; int i1, i2; char c[4]; } UNI;
Количество элементов в объединении с именем UNI и их типы совпадают с количеством и типами элементов в структуре STR. Но существует одно очень важное отличие - все элементы объединения имеют один и тот же начальный адрес размещения в памяти. То есть объединение можно расматривать как структуру, все элементы которой при размещении в памяти имеют нулевое смещение от начала. Тем самым все элементы объединения размещаются в одном и том же участке памяти. Размер участка памяти, выделяемого для объединения, определяется максимальной длиной его элементов. Как и для структур, для объединений может быть введен программистом производный тип, определяющий "внутреннее строение" всех объединений, относящихся к этому типу.
union имя_объединяющего_типа { элементы_объединения };
Пример объединяющего типа: union mixture { double d; long E[2]; int K[4]; };
Введя тип объединения, можно определять конкретные объединения, их массивы, а также указатели и ссылки на объединения:
mixture mA, mB[4]; // Объединение и массив объединений
mixture *pmix; // Указатель на объединение
mixture& rmix = mA; // Ссылка на объединение
Для обращения к элементу объединения можно использовать либо уточненное имя имя_объединения.имя_элемента, либо конструкцию, включающую указатель: указатель_на_объединение -> имя_элемента; (*указатель_на_объединение).имя_элемента, либо конструкцию, включающую ссылку ссылка_на_объединение. имя_элемента
mA.d = 64.8; mB[2]. E[1] = 10L;
pmix = &mB[0]; pmix -> E[0] = 66;
cin >> ( *pmix ).K[1]; cin >> rmix. E[0];
Заносить значения в участок памяти, выделенный для объединения, можно с помощью любого из его элементов. То же самое справедливо и относительно доступа к содержимому учаска памяти, выделенного для объединения. Основное достоинство объединения - возможность разных трактовок одного и того же содержимого (кода) участка памяти. Например, введя объединение union { float F; unsigned long K; } FK; можно занести в участок памяти, выделенный для объединения FK, вещественное число: FK.F = 3.141593; а затем рассматривать код его внутреннего представления как некоторое беззнаковое длинное целое: cout << FK.K; ( в данном случае будет выведено 10785330012).
Как массивы, так и структуры могут быть элементами объединений, причем здесь возможны весьма разнообразные сочетания. При определении конкретных объединений разрешена их инициализация, причем инициализируется только первый элемент объединения.
union compound { long LONG;
int INT[2]; char CHAR[4]; };
compound mix1 = { 11111111 }; // правильно
compound mix2 = { ‘a’, ‘b’, ‘c’, ‘d’ }; // ошибка
union { char CHAR[4];
long LONG;
int INT[2] } mix = { ‘a’, ‘b’, ‘c’, ‘d’ }; // правильно
При определении объединений без явного указания имени объединяющего типа (как в последнем примере для объединения mix) разрешено не вводить даже имени объединения. В этом случае создается анонимное или безымянное объединение: union { int INT[5]; char CH[10] } = { 1,2,3,4,5 };. К элементам анонимного объединения можно обращаться как и к отдельным объектам, но при этом могут изменяться другие элементы объединения:
INT[0] = 10; // изменятся значения CH[0], CH[1]
CH[9] = ‘a’; // изменится значение INT[4]
Разрешено формировать массивы объединений и инициализировать их: compound mixture[ ] = { 1L, 2L, 3L, 4L };
Здесь для каждого элемента mixture[i] введенного массива из четырех объединений типа compound инициализация выполнена для первого компонента объединения, то есть начальное значение, явно получил каждый элемент mixture[i].LONG. Доступ к внутренним кодам этих значений возможен также через элементы mixture[i].INT[j] и mixture[i].CHAR[k].
