- при возвращении результата используется имя переменной,которая заменяется транслятором неявной ссылкой на нее;
- примеры 2 и 3 идентичны по реализации, но отличаются по синтаксису вызова функции;
- примеры 1 и 3 отличаются по реализации, но идентичны по синтаксису вызова функции;
- из предыдущего следует, что при вызове функции список фактический параметров недостаточен для определения транслятором способа их передачи (значением или ссылкой), поэтому в Си++ для каждой внешней функции необходимо задать прототип.
Так как размер структуры, передаваемой в качестве результата функции, может быть сколь угодно большим, то для его хранения необходимо создать временную переменную. Транслятор "Borland C" в этом случае поступает следующим образом:
- при входе в функцию в стеке предполагается существование неявного параметра - "длинной" ссылки на структуру, в которой размещается результат функции;
- при выполнении операции return(x), где x - локальная переменная или формальный параметр, выполняется побайтовое копирование переменной x по адресу, заданному неявным параметром;
- если результат функции непосредственно присваивается другой переменной-структуре, то при вызове такой функции в стек помещается неявный параметр - ссылка на переменную в левой части операции присваивания;
- во всех остальных случаях в вызывающей функции создается по одной неявной автоматической переменной на каждый вызов функции, возвращающей структуру в качестве результата, а при вызове передается соответствующая ссылка на эту переменную-структуру. Такие переменные имеют все свойства автоматических, они существуют все время работы вызывающей функции, возможно даже получить ссылку на такую переменную.
Программа на Си++ Реализация
----------------- ---------- -- неявный параметр
dat Inc(dat x) void Inc(dat *r,dat x)
{ {
x.day++; x.day++;
return(x); *r = x; // Копирование
} } // результата
void main() void main()
{ {
dat a,b*p; dat a,b,*p;
dat t,u; // Временнye переменнye
a = Inc(b); Inc(&a,b); // Ссылка на левую часть
p = &Inc(b); Inc(&t,b); // Присаивание временной
p = &t; // переменной и получение
a = *p; a = *p; // ссылки на нее
a = Inc(Inc(b)); Inc(&u,b); // Промежуточный результат
Inc(&a,u); // во временной переменной
} }
2.3. Функции - элементы структуры
-------------------------------- Повторим рассмотренный выше пример в другой форме:
//------------ структура dat - аналог класса объектов "дата" --struct dat
{
unsigned day;
unsigned month;
unsigned year;
int TestData();
void NextData();
void PlusData(int n)
{
while(n-- !=0) dat::NextData(this);
}
};
//----------- набор функций для класса объектов "дата" --------static int mm[] = {31,28,31,30,31,30,31,31,30,31,30,31};
//----------- Проверка на корректность -----------------------int dat::TestData()
{
if (month ==2 && day==29 && year %4 ==0) return(1);
if (month ==0 || month >12 || day ==0 || day >mm[month])
return(0);
return(1);
}
//----------- Следующая дата ----------------------------------void dat::NextData()
{
day++;
if (day <= mm[month]) return;
if (month ==2 && day==29 && year %4 ==0) return;
day=1;
month++;
if (month !=13) return;
month=1;
year++;
}
//--------- Основная программа --------------------------------void main()
{
dat a;
do
{
scanf("%d%d%d", &a.day, &a.month, &a.year);
}
while(a.TestData() ==0);
a.PlusData(17);
}
//------------------------------------------------------- Как видно из примера, в качестве элементов структуры могут
выступать функции. Такие элементы-функции имеют следующие особенности:
- тело функции может быть определено в самой структуре (функция PlusData). В этом случае функция имеет стандартный вид;
- в определении структуры дается только прототип функции(заголовок с перечислением типов формальных параметров). Определение самой функции дается отдельно, при этом полное имя функции
имеет вид
<имя структуры>::<имя функции>
- в теле фукции неявно определен один формальный параметр с
именем this - ссылка на структуру, для которой вызывается функция
(В нашем примере это будет struct dat *this ). Поля этой структуры доступны через явное использование этой ссылки
this->month = 5;
this->day++;
или неявно
month = 5;
day++;
- для переменной, имеющей тип некоторой структуры, вызов
функцииэлемента этой структуры имеет вид
<имя переменной>.<имя функции> ( <список параметров> )
2.4. Переопределение функций
--------------------------- В Си++ возможно определение нескольких функций с одинаковым
именем, но с разными типами формальных параметров. При этом компилятор выбирает соответствующую функцию по типу фактических параметров. Переопределяемую функцию необходимо объявить с ключевым
словом overload:
overload SetDat;
void SetDat(int dd,int mm,int yy,dat *p)
{ // Дата вводится в виде трех целых
p->day=dd;
p->month=mm;
p->year=yy;
}
void SetDat(char *s,dat *p) // Дата вводится в виде строки
{
sscanf(s,"%d%d%d", &p->day, &p->month, &p->year);
}
void main()
{
dat a,b;
SetDat(12, 12, 1990, &a); // Вызов первой функции
SetDat("12,12,1990", &b); // Вызов второй функции
}
Функции-элементы также могут быть переопределены, при этом явного объявления не требуется.
struct dat
{
int day,month,year;
void SetDat(int,int,int);
void Setdat(char *);
}
void dat::SetDat(int dd,int mm,int yy)
{
day=dd; month=mm; year=yy;
}
void dat::SetDat(char *s)
{
sscanf(s,"%d%d%d",&day,&month,&year);
}
void main()
{
dat a,b;
a.SetDat(12,12,1990);
b.SetDat("12,12,1990");
}
2.5. Операторы управления динамической памятью
---------------------------------------------
В библиотеке Си имеются две функции управления динамической памятью - malloc() и free(), которые выделяют и освобождают область памяти заданного размера (в байтах). В этой области программа может разместить переменную (или массив), которая называется динамической. При выделении памяти под динамическую переменную необходимо при помощи операции sizeof определять количество байтов, необходимое для размещения переменной указанного типа. В Си++ введены два оператора, аналогичные функциям malloc и free new и delete. Они отличаются от соответствующих функций тем, что допускают использования в качестве аргументов непосредственно спецификацию типа создаваемой динамической переменной и ссылки на динамическую переменную:
Си++ "Классический" Си
------------------------- ---------------------------------char *s,x[80]; char *s,x[80];
dat *p,*q; struct dat *p,*q;
void main() void main()
{ {
p = new dat; p = malloc(sizeof (struct dat));
q = new dat[15]; q = malloc(15*sizeof (struct dat));
gets(x); gets(x);
s = new char[strlen(x)+1]; s = malloc(strlen(x)+1);
... ...
delete p; free(p);
delete q; free(q);
delete s; free(s);
}
Операторы имеют вид:
<результат: ссылка на <абстрактный
динамическую переменную> new описатель типа>
delete <ссылка на динамическую
переменную>
2.6. Параметры функций по умолчанию
----------------------------------
При определении формальных параметров функции может быть
указано его значение, принимаемое при вызове по умолчанию при
отсутствии этого параметра в списке фактических:
//----- Функция устанавливает по умолчанию текущее значение года,
//----- месяца и дня
#include <dos.h>
void dat::SetDat(int d=0, int m=0, int y=0)
{
struct date x;
getdate(&x); // Стандартная функция получения
// текущей даты
// Проверка на значение по умолчанию
year = (y == 0) ? x.da_year : y;
month= (m == 0) ? x.da_month: m;
day = (d == 0) ? x.da_day : d;
}
2.7 Контроль преобразования типов ссылок
---------------------------------------
В "классическом" Си при выполнении присваивания, передаче фактических параметров происходит автоматическое преобразование ссылок к базовым типам данных (int,unsigned) и наоборот, а также преобразование одного типа ссылки к другому. В Си++ такие "вольности" исключены, программист должен сам выполнить явное преобразование. Например, при использовании функции распределения динамической памяти, имеющей прототип в "alloc.h"
extern void* malloc(int n);
dat *p;
p = (dat *) malloc (10*sizeof(dat));
¦
L--- преобразование void* в dat*
Естественно, что это преобразование типов фиктивное в том смысле, что не меняет значения ссылки и не приводит к генерации кода. Оно только меняет "точку зрения" транслятора на данную ссылку.
2.8 Вставляемые (inline) функции
-------------------------------
Если функция (обычная или элемент-функция структуры или класса) объявлены inline-функциями, то при вызове таких функций транслятор выполняет подстановку по тексту программы тела функции с соответствующей заменой формальных параметров на фактические. Элемент-функция также считается inline по умолчанию, если ее тело определено непосредственно в определении структуры (или класса),например:
struct dat
{
int d,m,y;
void Setdat(char *p) // Функция inline по умолчанию
{
... // Тело функции
}
2.9 Ссылки на элементы структуры
-------------------------------
Если структура имеет несколько элементов одного типа,то для нее может быть создана "внутренняя" ссылка, которая принимает значение внутреннего адреса (смещения) элемента относительно выбранной структуры. Формирование и использование такой ссылки ясно
из примера:
struct dat
{
int day,month,year;
void Getdat();
void Putdat();
void Nextdat();
}
int dat::*p; // Ссылка на элемент типа int
// в структуре dat
p = & dat::month; // Значение p - смещение (адрес)
// элемента month в структуре типа
// dat
dat x,*px = &x; //
x.*p = 5; // Обращение по внутренней ссылке
px->*p = 5; // <dat> . *<ссылка на элемент>
// <*dat>-> *<ссылка на элемент>
Эквивалентно
x.month = 5;
px->month =5;
Аналогичная внутренняя ссылка может быть создана для элементов-функций, принадлежащих одной структуре, при этом функции
должны быть идентичными по результатам и параметрам:
void (dat::*fun)(); // Ссылка на элемент-функцию
// структуры dat
fun = & dat::Putdat(); // Значение fun - ссылка на
// элемент-функцию Putdat в dat
(x.*fun)(); // Вызов элемента-функции по
(px->*fun)(); // ссылке fun для структуры x
// и для структуры по ссылке px
Эквивалентно
x.Putdat();
px->Putdat();
2.10 Неизменяемые переменные (константы)
---------------------------------------
В Си++ введен дополнительный контроль за изменением значений переменных. Ключевое слово const, используемой при определении и инициализации переменной, запрещает ее изменение, что контролируется транслятором при ее дальнейшем использовании. Такая же возможность существует и для формальных параметров функции, например: