Студопедия
Случайная страница | ТОМ-1 | ТОМ-2 | ТОМ-3
АрхитектураБиологияГеографияДругоеИностранные языки
ИнформатикаИсторияКультураЛитератураМатематика
МедицинаМеханикаОбразованиеОхрана трудаПедагогика
ПолитикаПравоПрограммированиеПсихологияРелигия
СоциологияСпортСтроительствоФизикаФилософия
ФинансыХимияЭкологияЭкономикаЭлектроника

Передача параметров и возвращаемое значение

Читайте также:
  1. A) Informations – Передача информация
  2. I. Найдите слова из первой колонки в тексте и соотнесите с их значением во второй колонке.
  3. IV. Передача персональных данных субъектов ПД
  4. Section 1. Передача имен собственных и географических названий.
  5. VII Вселенский собор и его историко-догматическое значение
  6. А. Префиксы с отрицательным значением
  7. АДСОРБЕРЫ С ПСЕВДООЖИЖЕННЫМ СЛОЕМ АДСОРБЕНТА. НАЗНАЧЕНИЕ, УСТРОЙСТВО И ПРИНЦИП ДЕЙСТВИЯ.

Лекция 7. Функции – итоги и некоторые дополнительные, ранее упущенные, сведения

Содержание. Определение функции и прототип. Передача параметров и возвращаемое значение. Параметры по умолчанию. Встроенные (inline) функции. Оператор asm. Рекурсивные функции, пример. Перегруженные функции (полиморфизм функций). Указатели на функции. Замечание о синтаксисе указателей.

Напомним некоторые общие положения, касающиеся функций.

 

Определение функции и прототип.

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

 

(i) Для указания того, что функция будет определена позже или в другом исходном файле.

 

(ii) Используется в заголовочном файле для описания интерфейса функции.

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

 

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

 

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

Передача параметров и возвращаемое значение

 

Параметр может быть передан в функцию либо по значению, либо по ссылке. Если он передается по значению, Если он передается по значению, то формальный параметр становится копией фактического параметра. Если – по ссылке, то формальный параметр есть алиас (alias), т.е. другое имя фактического параметра. Если мы собираемся изменять значение фактического параметра внутри функции, то параметр лучше передать по ссылке. Если мы не будем менять фактического параметра, то лучше передать его по значению. Для того, чтобы не копировать “большие” по размеру аргументы (фактические параметры), например, типы-агрегаты, и не давать функции возможности изменять аргумент, используется передача по константной ссылке (модификатор const).

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

 

Пример:

 

// демонстрирует константные аргументы функций

void aFunc(int& a, const int& b); // прототип функции

int main()

{

int alpha = 7;

int beta = 21;

aFunc(alpha, beta);

return 0;

}

//--------------------------------------------------------

void aFunc(int& a, const int& b) // определение функции

{

a = 106; // корректно

b = 777; // ошибка при попытке

// изменить константный аргумент

}

 

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

 

Если параметр передается по значению или по константной ссылке, то фактический параметр может быть r-value: выражением или константным литералом, таким как int 3 или строка "string". Если параметр передан по неконстантной ссылке, то аргумент должен быть l-value (лево-допустимым выражением).

 

Если имя массива передается функции, то оно преобразуется в указатель на первый элемент массива, при этом формальный параметр может писаться как массив или указатель. Но какая бы нотация не использовалась, формальный параметр – указатель. В частности, когда в функцию передается N-мерный массив, формальный параметр – это указатель на первый элемент N-мерного массива, который (т.е. элемент) в С++-понимании (“массив массивов”) есть (N-1)-мерный массив. Поскольку формальный параметр – указатель на первый элемент массива, то сами элементы массива могут быть модифицированы внутри функции с использованием этого указателя. Даже когда формальный параметр пишется как массив (идентификатор массива), число элементов неизвестно в функции (мы должны отдельно об этом позаботиться).

Вот любопытный простой пример, демонстрирующий передачу массива в функцию:

//

// Pointers_As_Parameters.cpp

//

#include <iostream>

using namespace std;

void swap(int [], int, int);

void swap(int A[], int j, int k)

{

int temp = A[j];

A[j] = A[k];

A[k] = temp;

}

int main(void)

{

int b[8] = {1,2,3,4,5,6,7,8};

cout << "b: ";

for (int i=0; i<8; i++)

cout << " " << b[i];

cout << endl;

swap(b,2,3);

cout << "b: ";

for (i=0; i<8; i++)

cout << " " << i[b];

cout << endl;

swap(b,0,7);

cout << "b: ";

for (i=0; i<8; i++)

cout << " " << *(b+i);

cout << endl;

return 0;

}

 

Функция может возвращать значение, ссылку или указатель. Если она возвращает значение, то в функции под него создается временная переменная. Если тип возвращаемого значения относится к одному из простых типов, то возвращаемое значение не может использоваться как l-value. Если возвращаемый тип – агрегатный, такой, как структура, или объект класса (как мы увидим позже), то возвращаемое значение МОЖЕТ использоваться как l-value. Поскольку в большинстве случаев нет смысла использовать временную переменную как l-value, мы обычно специфицируем возвращаемо значение константного типа. Если функция возвращает ссылку, то возвращаемая ссылка, т.е. сам вызов функции может быть использовано как l-value (см. Пример 1-3.1). Если функция возвращает ссылку или указатель, то желательно проверять, что объекты для ссылки или указателя правильные и существуют после выхода из функции. Функции не могут возвращать массив.

 

И еще одно замечание о передаче параметров в функцию. В план нашего курса, вообще говоря, не входит достаточно подробное изучение библиотеки STL. Но обычная практика программирования на С++ предполагает опережающее использование контейнерных типов как STL, так тех, которые поставляются конкретной реализацией С++, будь то С++ Builder или VC++.

Абстрактные контейнерные типы, также используются для объявления параметров функции. Например, можно определить некоторую функцию putValues() как имеющую параметр типа vector<int> вместо встроенного типа массива.

Контейнерный тип является классом и обеспечивает значительно большую функциональность, чем встроенные массивы. Так, vector<int> “знает” собственный размер. При передаче обычного массива размер параметра-массива неизвестен функции и для его передачи приходится задавать дополнительный параметр. Использование vector<int> позволяет обойти это ограничение. Фрагмент кода внизу иллюстрирует этот случай:

#include <iostream>

#include <vector>

const lineLength =12; // количество элементов в строке

void putValues(vector<int> vec)

{

cout << "(" << vec.size() << ")< ";

for (int i = 0; i < vec.size(); ++1) {

if (i % lineLength == 0 && i)

cout << "\n\t"; // строка заполнена

cout << vec[ i ];

// разделитель, печатаемый после каждого элемента,

// кроме последнего

if (1 % lineLength!= lineLength-1 &&

i!= vec.size()-1)

cout << ", ";

}

cout << " >\n";

}

Функция main(), вызывающая нашу функцию putValues(), выглядит так:

void putValues(vector<int>);

int main() {

int i, j[ 2 ];

// присвоить i и j некоторые значения

vector<int> vec1(1); // создадим вектор из 1 элемента

vecl[0] = i;

putValues(vecl);

vector<int> vec2; // создадим пустой вектор

// добавим элементы к vec2

for (int ix = 0;

ix < sizeof(j) / sizeof(j[0]);

++ix)

// vec2[ix] == j [ix]

vec2.push_back(j[ix]);

putValues(vec2);

return 0;

}

Заметим, что параметр функции putValues() передается по значению. В подобных случаях контейнер со всеми своими элементами всегда копируется в стек вызванной функции. Поскольку операция копирования весьма неэффективна, такие параметры лучше объявлять как ссылки. Например, если функция не модифицирует значение своего параметра, то предпочтительнее, чтобы он был ссылкой на константный тип:

void putValues(const vector<int> &) {...


Дата добавления: 2015-11-16; просмотров: 63 | Нарушение авторских прав


<== предыдущая страница | следующая страница ==>
Шаблоны функций| Рекурсивные Функции

mybiblioteka.su - 2015-2024 год. (0.013 сек.)