Ссылки. Передача аргументов в функции по ссылке. 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Ссылки. Передача аргументов в функции по ссылке.



Тип "ссылка на тип" определяется так: тип&, например: int& или double&

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

Рассмотрим инициализацию ссылки: int i=0; int& iref = i;

Здесь создана новая переменная типа ссылка на int с именем iref. Физически iref - это постоянный указатель на int и, следовательно, значение ссылки после инициализации не может быть изменено. Инициализирующим значением в нашем случае является адрес переменной i, т.е. при инициализации ссылка ведёт себя как указатель.

При использовании ссылка ведёт себя не как указатель, а как переменная, адресом которой она инициализирована:

iref ++; // то же, что и i++;

int *ip=&iref; // то же, что и ip=&i.

Итак, iref стало другим именем, псевдонимом переменной i.

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

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

double d=0.0;

int& ir = d; // Создан анонимный объект типа int;

ir = 3.0; // d - не меняется!

Здесь создаётся анонимная переменная типа int, которая инициализируется значением, полученным в результате преобразования значения типа double к типу int. Затем ссылка инициализируется значением адреса этой переменной.

Анонимный объект создаётся также, когда инициализатор не является объектом, например, является константой: int& ir = 3; // Анонимный объект получил значение 3.

Здесь сначала создается анонимный объект типа int и он инициализируется значением 3. После этого создаётся ссылка ir и инициализируется адресом анонимного объекта. Теперь ir - его псевдоним и оператор

ir = 8; устанавливает новое значение этого анонимного объекта.

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

void swap (int & x, int & y){

int t = x;

x = y;

y = t; }

Теперь обращение в вызывающей функции имеет вид:

int a = 3, b = 7;

swap (a, b);

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

Заметим, что в последнем примере можно обратиться к функции swap() и с аргументами, отличными по типу от int, и даже с аргументами, не являющимися объектами:

float a = 5, b = 2.7;

swap (a, b);

swap (3, a+b);

Однако в этих случаях функция swap() фактически никакого действия со своими аргументами не выполняет. Создаются временные объекты типа int, которые инициализируются значениями, полученными в результате преобразования a, b, a+b к типу int; затем ссылки x и y инициализируются значениями адресов этих анонимных объектов; анонимные объекты и будут изменены. Фактические параметры же останутся неизменными. Компилятор выдаст предупреждение, что он вынужден завести временные переменные, и будет работать с ними.

 

Функции. Аргументы по умолчанию и переопределение функций.

Аргументы по умолчанию

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

void error (char* msg, int level = 0, int kill = 0);

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

error ("Ошибка!"); // Вызывается error ("ошибка", 0, 0);

error ("Ошибка!", 1); // вызывается error ("ошибка", 1, 0);

error ("Ошибка!", 3, 1); // значения аргументов по умолчанию не используются.

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

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

int i=8; void func (int = i);

Заметим, что если инициализация аргументов произведена в прототипе функции, в определении функции задавать инициализацию аргументов не надо.

Переопределение функций

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

Пусть объявлены следующие функции:

int func(int, int);

int func(char, double);

int func(long, double);

int func(float, …); // Функция с неопределенным числом аргументов.

int func(char*, int);

Рассмотрим, что будет происходить при вызове функции с именем func с некоторым списком аргументов.

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

char string[ ]="Строка - это массив символов";

int i=func (string, 13); // func (char*, int);

int j=func(1995L, 36.6); // func(long, double);

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

Пусть обращение к функции выглядит так: float a=36.6; j=func('a', a);

Применяя указанные стандартные преобразования, найдём, что будет вызвана функция с прототипом 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); компилятор не найдёт ни одной подходящей функции и выдаст сообщение об ошибке.

Шаблоны функций.

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

В определении шаблонов семейства функций используется служебное слово 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 < iostream.h>

template < class type>

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 cout<< "\t x["<< i<< "]="<< x[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["<< i<< "]="<< f[i];

}

Результат выполнения программы: r_max (n, x)=30 x[0]=10 x[1]=20 x[2]=0 x[3]=5

r_max (3, f)=50.7 f[0]=10.3 f[1]=0 f[2]=12.6

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

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

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

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

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

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

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

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

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

template < class type>

void swap (type&, type&);

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

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

int n=5;

double d=4.3;

swap (n, d);

 

 

Перечисления

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

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

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

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

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 и month, то первый из них получает значение 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;

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



Поделиться:


Последнее изменение этой страницы: 2016-08-15; просмотров: 422; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.119.159.150 (0.032 с.)