WWW.DISSERS.RU

БЕСПЛАТНАЯ ЭЛЕКТРОННАЯ БИБЛИОТЕКА

   Добро пожаловать!


Pages:     | 1 |   ...   | 5 | 6 || 8 | 9 |   ...   | 17 |

Применяя указанные стандартные преобразования, найдём, что будет вызвана функция с прототипом func(char, double) и аргумент а будет преобразован к типу double.

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

Так, в операторе int l=func(“ГОД:”, 2002.3);

будет вызвана функция func (char*, int), фактический аргумент типа double которой будет преобразован к int с отбрасыванием дробной части числа.

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

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

Так, при вызове func (1, 2, 3);

подходит лишь одна функция func(float, …).

При обращении int i, j, n;

… n=func(&i, &j);

компилятор не найдёт ни одной подходящей функции и выдаст сообщение об ошибке.

16.10. Шаблоны функций Цель введения шаблонов функций – автоматизация создания функций, которые могут обрабатывать разнотипные данные.

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

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

template type abs(type x){return x >0 x: -x;} Шаблон функций состоит из двух частей – заголовка шаблона и из обычного определения функции, в котором тип возвращаемого значения и типы любых параметров и локальных переменных могут обозначаться именами параметров шаблона, введённых в его заголовке.

Пример (снова функция swap):

template void swap(T& x, T& y){T z = x; x = y; y = z;} Шаблон семейства функций служит для автоматического формирования конкретных определений функций по тем вызовам, которые транслятор обнаруживает в тексте программы. Например, при обращении abs(-10, 3) компилятор сформирует такое определение функции:

double abs(double x){return x > 0 x: -x;} Далее будет организовано выполнение именно этой функции и в точку вызова в качестве результата вернется значение 10.3.

Пример: шаблон функций для поиска в массиве максимального элемента.

#include // Функция устанавливает ссылку // на элемент с максимальным значением template type & r_max ( int n, type d[ ] ){ int im=0;

for (int i = 1; i < n; i++) im = d[im] > d[i] im : i;

return d[im];

} void main(){ int n = 4, x[ ]={10, 20, 30, 5};

cout<<”\n r_max (n, x)=”<< r_max (n, x); // Печать максимального // элемента.

r_max (n, x) = 0; // Замена в целом массиве // максимального элемента нулем.

for (int i=0; i

float f[]={10.3, 50.7, 12.6};

cout<<”\n r_max (3, f)=”<< r_max (3, f);

r_max (3, f)=0;

for (i=0; i<3; i++) cout<<”\t f[“<

} Результат выполнения программы:

r_max (n, x)=30 x[0]=10 x[1]=20 x[2]=0 x[3]= r_max (3, f)=50.7 f[0]=10.3 f[1]=0 f[2]=12.При использовании шаблонов уже нет необходимости готовить заранее все варианты функций с перегруженным именем. Компилятор автоматически, анализируя вызовы функций в тексте программы, формирует необходимые определения именно для таких типов параметров, которые использованы в обращении.

Перечислим основные свойства параметров шаблона.

Имена параметров шаблона должны быть уникальными во всём определении шаблона.

Список параметров шаблона функций не может быть пустым.

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

Недопустимо использовать в заголовке шаблона параметры с одинаковыми именами.

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

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

Заметим, что при необходимости можно использовать прототипы шаблона функций. Например, прототип функции swap():

template void swap (type&, type&);

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

Так, недопустимо:

int n=5;

double d=4.3;

swap (n, d);

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

Может быть и так, что их значение само по себе не важно.

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

Задание типа перечисление начинается ключевым словом enum, после которого идёт имя типа (иногда называемое тегом), за которым в фигурных скобках следует список членов перечисления – перечислителей:



enum chess {king, queen, rook, bishop, knight, p};

enum month {Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec};

enum operator_CPP{plus=’+’, minus=’-’, mult=’*’, div=’/’, rem=’%’};

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

Если перечислителям явно не присвоено никакого значения, как в chess и mouth, то первый из них получает значение 0, второй – 1, и т.д.

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

Все члены перечисления operator_CPP получают явно указанные значения, равные ASCII – кодам символов ‘+’, ‘-’, ‘*’, ‘/’, ‘%’.

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

const TWO = 2;

enum {first, second=first, next=first+TWO, last =next*next+1}dummy;

Отметим, что в последнем случае не вводится тег перечисления, а сразу описывается переменная dummy, которая в соответствии с шаблоном перечисления может принимать одно из четырех значений:

first, second, next, last.

Следовательно, возможны присваивания dummy = first;

dummy = last;

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

Так, если сделать описания month m1, m2;

operator_CPP op1, op2, op3;

enum colour { red, green} c[10];

то переменным op1, op2, op3 можно присваивать значения plus, minus,...; m1, m2 – Jan, Feb и т. д., а элементам массива c[0]... c[9] – значения red и green.

Перечислитель может появиться везде, где разрешено появление значения типа int. Обратное неверно без явного преобразования типа.

int i = dummy; // i = = month current_month = Jan;

current_month =0; // Ошибка! Будет выдано предупреждение.

current_month = ( month )0; // Теперь верно! Feb = 1; // Ошибка: Feb – константа! Пример:

# include < iostream.h > const NUMDAYS = 7;

enum DAYS { mon, tue, wen, thur, fri, sat, sun } day1, day2, day3;

DAYS day_before ( DAYS ), day_after ( DAYS );

void print_day ( DAYS );

void main ( ) { day1 = sun;

day2 = day_after ( day1 );

day3 = day_before ( day1);

cout << “Если сегодня“;

print_day ( day1 );

cout << “, то завтра будет ”;

print_day ( day2 );

cout <<“,\n а вчера был день ”;

print_day ( day3 );

cout <<“.\n”;

} DAYS day_after (DAYS day ){ return (( DAYS ) (( day +1 )% NUMDAYS) );

} DAYS day_before ( DAYS day ) { int prev = (day-1 )%NUMDAYS ;

return ( prev <0 ) ( NUMDAYS – 1): prev ;

} void print_day ( DAYS day ) { int day_i = day;

static char * days [ ] ={ “Понедельник”, “Вторник”, “Среда”, “Четверг”, “Пятница”, “Суббота“, “Воскресенье” };

if ( day_i <0 || day_i > NUMDAYS ) cout << “ Ошибка! \ n” ; else cout << days[ day_i ] ;

} Результат выполнения:

Если сегодня воскресенье, то завтра будет Понедельник, а вчера была Суббота.

18. Классы 18.1. Объявление классов Тип данных класс можно определить с помощью конструкции ключ_класса, имя_класса{ список _членов };

Здесь ключ_класса – одно из служебных слов struct, union, class;

имя_класса – произвольный идентификатор;

список_членов – определения и описания членов класса – данных и функций.

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

Пример структуры – учётная карточка сотрудника, в которой содержится фамилия, имя, отчество, адрес, должность, год поступления на работу и т.д. Некоторые из этих атрибутов сами могут оказаться структурами. Так, Ф.И.О. имеет три компоненты, адрес – также несколько компонент.

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

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

Введем новые типы FIO и sotrudnik:

struct FIO{ char familia [39], imya [30], otchestvo [30] };

struct sotrudnik{ FIO name;

char dolgnost [30];

int year;

float oklad };

Здесь заданы два новых типа структурных переменных и имена этих типов – FIO, sotrudnik. Заметим, что наличие «;» после фигурных скобок здесь обязательно.

Теперь можно объявить структурные переменные типа FIO или sotrudnik обычным образом:

FIO name1, name2, name3;

sotrudnik s1, s2, s[50];

Теперь компилятором будет выделена память под переменные name1, name2, name3, s1, s2 и под массив s из пятидесяти структур.

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





Чтобы определить выделенное число байтов, надо воспользоваться операцией sizeof, например, так:

int nf=sizeof (FIO), ns=sizeof (sotrudnik);

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

struct DATE { int day;

int month;

int year;

char mon_name[4] } d1, d2, d3;

Здесь объявлены три переменных d1, d2, d3, которые имеют тип структуры DATE.

Можно объявить структурную переменную и без введения имени (тега) структуры:

struct {int price;

float length [10] } a, b, c, d;

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

a.price c.length d1.dayd3.mon_name s[25].oklads[0].name.familia.

Имена наподобие c.length, d1.dayd3.mon_name, с помощью которых происходит доступ к членам класса, иногда называют уточненными именами. Если определить указатель на структуру, DATE* datep=&d1, то обратиться к члену структуры d1 можно так: (*datep).year, или с помощью операции извлечения из указателя на структуру -> так datep>year, что эквивалентно.

Введем теперь простейший класс “комплексное число”:

struct compl{ double real, imag;

void define ( double re = 0.0, double im = 0.0 ){ real = re; imag = im; // задание комплексного числа.

} void display(){cout<<“real = ”<

Здесь real, imag – данные-члены или компоненты, а define(), display() – функции-члены или компонентные функции, которые часто называют методами класса.

Теперь можно описать объекты типа compl:

compl a, b, c, *pc = &c;

После этих определений данные-члены структурных переменных доступны в области их видимости:

a.define (3, 7); // Определяется комплексное число 3+7i, // т.е. a. real = 3; a.imag = 7;

b.define (2); // определяется комплексное число 2+0*i = 2;

c.define ( ); // комплексное число = 0;

// оба параметра выбираются по умолчанию.

Данные-члены можно задавать и использовать непосредственно, не через функции define( ), display( ):

a.real = 3; a.imag = 7; (*pc).real = 1; pcimag = -1;

a.real+ = b.real*3+7;

cout <<“pcreal : ” <

a.display ( );

b.display ( );

c.display ( );

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

class complex { double real, imag;

public:

void display( ) { cout <<“ real =” <

cout <<“, imag =” << imag <<‘\n‘;

} void define ( double re = 0.0, double im = 0.0){ real = re; imag = im;

} };

Метка public, которая может присутствовать в объявлении класса, в нашем примере делит его тело на две части – “личную”, или ”собственную” (private) и общую – (public).

Доступ к данным-членам класса, находящимся в собственной части, возможен лишь через функции-члены класса:

complex s1, s2, *ps = &s1;

s1.define ( ); // s1.real=0; s1.imag=0;

s1.display ( ); // Выводится real=0, imag=0;

ps->display ( ); // то же самое.

s1.real=3; // Ошибка! private-член s2.real недоступен! В определении класса может также явно присутствовать метка private.

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

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

Основное отличие struct и class состоит в уровне доступа по умолчанию. Если нет явного указания уровня доступа, то все члены структуры считаются общими, а класса – собственными. Явное указание уровней доступа делает слова struct и class равнозначными. Обычно использование слова struct вместо class говорит о том, что в ограничении уровня доступа к данным нет необходимости (здесь предполагается, что все члены структуры общие).

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

18.2. Конструкторы В предыдущем примере инициализация объектов типа complex производилась с помощью функции-члена define ( ). При этом переменная s2 осталась неинициализированной. В С++ предусмотрены специальные функции-члены класса, которые в большинстве случаев вызываются не программистом, а компилятором и которые предназначены для инициализации объектов абстрактных типов. Такие функции называются конструкторами. Рассмотрим пример:

class cl { int num;

public:

void set (int i) { num = i; } void show ( ) { cout <<“Число: “ << num <<‘\n‘; } };

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

void f( ){ cl obj; // Объект создан.

obj.set (10); / / Объект инициализирован.

obj.show ( ); //Объект можно использовать.

} Теперь используем для инициализации конструктор. Это просто специальная функция – член класса cl, имя которой обязательно совпадает с именем класса:

class cl{int num;

public:

cl ( int i ) { num = i ;} // Конструктор.

void show ( ) { cout << “Число:” << num << ‘\n‘;} };

Заметим, что для конструктора никогда не указывается тип результата! Функция, использующая этот класс, примет вид:

Pages:     | 1 |   ...   | 5 | 6 || 8 | 9 |   ...   | 17 |










© 2011 www.dissers.ru - «Бесплатная электронная библиотека»

Материалы этого сайта размещены для ознакомления, все права принадлежат их авторам.
Если Вы не согласны с тем, что Ваш материал размещён на этом сайте, пожалуйста, напишите нам, мы в течении 1-2 рабочих дней удалим его.