С++ для начинающих

       

Передача аргументов


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

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

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

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

Однако такой способ передачи аргументов может не устраивать нас в следующих случаях:

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

    void swap( int vl, int v2 ) {

        int tmp = v2;

        v2 = vl;

        vl = tmp;

    }

    swap() обменивает значения локальных копий своих аргументов. Те же переменные, что были использованы в качестве аргументов при вызове, остаются неизменными. Это можно проиллюстрировать, написав небольшую программу:




    #include <iostream>

    void swap( int, int );

    int main() {

        int i = 10;

        int j = 20;

        cout << "Перед swap():\ti: "

             << i << "\tj: " << j << endl;

        swap( i, j );

        cout << "После swap():\ti: "

             << i << "\tj: " << j << endl;

        return 0;

    }

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

    Перед swap():    i: 10    j: 20

    После swap():    i: 10    j: 20

    Достичь желаемого можно двумя способами. Первый – объявление параметров указателями. Вот как будет выглядеть реализация swap() в этом случае:

    // pswap() обменивает значения объектов,

    // адресуемых указателями vl и v2

    void pswap( int *vl, int *v2 ) {

        int tmp = *v2;

        *v2 = *vl;

        *vl = tmp;

    }

    Функция main() тоже нуждается в модификации. Вместо передачи самих объектов необходимо передавать их адреса:

    pswap( &i, &j );

    Теперь программа работает правильно:

    Перед swap():    i: 10    j: 20

    После swap():    i: 20    j: 10

    Альтернативой может стать объявление параметров ссылками. В данном случае реализация swap() выглядит так:

    // rswap() обменивает значения объектов,

    // на которые ссылаются vl и v2

    void rswap( int &vl, int &v2 ) {

        int tmp = v2;

        v2 = vl;

        vl = tmp;

    }

    Вызов этой функции из main() аналогичен вызову первоначальной функции swap():

    rswap( i, j );

    Выполнив программу main(), мы снова получим верный результат.


    Содержание раздела