Разница между передачей указателей на функции по значению и по ссылке

Я пытаюсь понять разницу между передачей указателей на функции с использованием значения и ссылки. В моем случае я «удаляю []» переданный указатель. Я предполагаю, что удаление указателя является формой модификации этого указателя. Поэтому, если я передам указатель на массив (скажем, ptr) функции по значению, мне не должно быть позволено «удалить [] ptr» внутри этой функции. Однако, когда я использую оба способа сделать это (передавая ptr по значению и по ссылке), компилятор позволяет мне удалить ptr внутри функции в обоих случаях.

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

// Understanding passing by value and reference for pointers

#include<iostream>

using namespace std;

int* createNew_value(int* _oarr, int &_size);
// createNew_value() appends an integer to the input integer array _oarr. The
// value of _size is incremented by one after the call to createNew_value. The
// pointer to array _oarr is passed by value.

int* createNew_reference(int* &_oarr, int &_size);
// Same functionality as createNew_value(), but here the pointer to array _oarr
// is passed by reference.

void displayIntArray(int* _iarr,int _size);
// Just diplays elements of the passed integer array.

int main()
{
    int *int_array;
    int size;

    cout << "Enter the number of elements:";
    cin >> size;

    int_array = new int [size];

    // Initialize elements of array to consecutive integers. This initialization
    // is only here to ensure that the elements of array are not undefined.
    // Other than that this initialization doesnt serve any purpose

    for (int j = 0; j <= size - 1; j++)
        int_array[j] = j;

    // Display the starting location and elements of the filled array;    
    cout << "[main()]: int_array [before createNew_value()] = " << int_array << endl;
    displayIntArray(int_array,size);

    // Display the starting location and elements of the filled array, after
    // call to createNew_value().

    int_array = createNew_value(int_array, size);

    cout << "[main()]: int_array [after createNew_value()] = " << int_array << endl;
    displayIntArray(int_array,size);

    // Display the starting location and elements of the filled array, after
    // call to createNew_reference().

    int_array = createNew_reference(int_array, size);

    cout << "[main()]: int_array [after createNew_reference()] = " << int_array << endl;
    displayIntArray(int_array,size);

    // Finally delete int_array to prevent memory leak. 
    delete [] int_array;

    return(0);
}
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
int* createNew_value(int* _oarr, int &_size)
// createNew_value() accomplishes the following: It creates a pointer to an
// integer array, called temp, and allocates enough memory for storing (_size +
// 1) elements. It then copies the elements of _oarr into temp, and appends one
// more integer to temp. It then deletes the original array pointer _oarr and
// returns temp. The return value of this function is a pointer to an array with
// one element larger than the input array
{
    int* temp;

    temp = new int [_size + 1];

    //copy elements of old array, _oarr, into temp

    for(int i = 0; i <= _size - 1; i++)
        temp[i] = _oarr[i];

    temp[_size] = temp[_size - 1] + 1;

    _size++;

    cout << "[createNew()]: _oarr = " << _oarr << endl;
    cout << "[createNew()]: temp = " << temp << endl;

    delete [] _oarr;

    // Since _oarr is passed by value, C++ SHOULDNT allow me to delete[] it !!

    // QUESTION: I am passing _oarr by value here. So why does C++ allow me to
    // delete [] it? Isnt deleting equivalent to modification? If yes, how can I
    // modify _oarr if I am passing it my value?

    return(temp);
}
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
int* createNew_reference(int* &_oarr, int &_size)
// createNew_reference() accomplishes the following: It creates a pointer to an
// integer array, called temp, and allocates enough memory for storing (_size +
// 1) elements. It then copies the elements of _oarr into temp, and appends one
// more integer to temp. It then deletes the original array pointer _oarr and
// returns temp. The return value of this function is a pointer to an array with
// one element larger than the input array
{
    int* temp;

    temp = new int [_size + 1];

    //copy elements of old array, _oarr, into temp

    for(int i = 0; i <= _size - 1; i++)
        temp[i] = _oarr[i];

    temp[_size] = temp[_size - 1] + 1;

    _size++;

    cout << "[createNew()]: _oarr = " << _oarr << endl;
    cout << "[createNew()]: temp = " << temp << endl;

    delete [] _oarr;
    return(temp);
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void displayIntArray(int* _iarr,int _size)
{
    cout << "{ ";

    for (int n = 0; n <= _size - 2; n++)
        cout << _iarr[n] << ", ";

    cout << _iarr[_size - 1] << " }\n";
}

person RDK    schedule 24.01.2012    source источник


Ответы (2)


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

person Neil    schedule 24.01.2012
comment
Ага, это проясняет. Спасибо. Повторюсь, если есть указатель p1 и я создаю его копии p2 = p1 и delete p2, то p1 и p2 оба висят. Я на правильном пути здесь? - person RDK; 25.01.2012
comment
Конечно. Альтернативой является использование одного из различных видов интеллектуальных указателей, которые справятся со скучной работой за вас. - person Neil; 25.01.2012

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

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

Помогает ли это вам лучше понять его?

person Scott Bonner    schedule 24.01.2012
comment
Что-то вроде. Я думал, что даже в этом случае есть две чашки, поскольку передача указателя по значению создаст еще один указатель, указывающий на местоположение исходного указателя. Однако это не так. Передача указателей по значению, похоже, не создает его копию. - person RDK; 25.01.2012