void do_some_calc(int arg)
{
int count = arg;
int prev = 1, prev_prev =0;
for(int i = 0; i < count; i++)
{
if(i < 1)
{
prev_prev = prev;
prev = arg;
}
arg = prev + prev_prev;
}
}
Эта программка должна вычислять arg-е число Фибоначчи и записывать его в тот же arg (да, способ не самый адекватный, проехали это).
Допустим, мы создали переменную int number = 3 и отправили её в процедуру do_some_calc(number). Но по выходу из процедуры переменная number не сможет подтвердить, что мы действительно что-то вычисляли, потому что значение её не изменится.
А всё потому, что передавалась не сама переменная, а её копия. И вычисленное значение, сохранённое в копии переменной number с именем arg, по выходу из процедуры просто удалилось вместе со всем, что использовалось в её теле.
Этого можно избежать, передавая параметр по ссылке или указателю. В таком случае, мы будем иметь дело не с копией переменной, а с адресом оригинала переменной. А доступ к значению и всё прочее описано парой постов ранее. Соответственным образом и не составляет труда переписать код процедуры, чтобы она таки начала работать на нас.
Так вот, теперь к главному. Допустим, мы приняли такой слегка странный стиль программирования и предпочли процедуру функции в следующей задаче: написать процедуру выделения памяти для указателя
void allocate_memory_for_ptr(int* ptr)
{
ptr = new int[1000];
}
Да, передаётся указатель int* tricky_pointer. Да, выделяется память для него allocate_memory_for_ptr(tricky_pointer). Но по выходу из процедуры доступ к tricky_pointer[0]..tricky_pointer[whatever] получить не удастся. Более того, в процедуре только что нашими руками была создана утечка памяти.
Как ни странно, а в allocate_memory_for_ptr(...) была передана копия указателя tricky_pointer, которой повезло так же, как и переменной arg из прошлого примера. Можно заключить, что всё (абсолютно всё, включая указатели) передаваемое параметром в функцию, просто копируется. И этого эффекта будет незаметно, если в процедуре меняется только содержимое указателя или не меняется вообще ничего, как в примере с суммой элементов массива (для этого, в общем-то, придумана константность, но о ней как-нибудь попозже). Когда же меняется сам адрес, лучше поступить вот так:
void allocate_memory_for_ptr(int*& ptr)
{
ptr = new int[1000];
}
А если говорить непосредственно о ссылках, то вот самая типичная процедура:
void change_ref(int& ref)
{
int p = 5;
ref = p;
}
При таком её использовании:
int i = 0;
int& r = i;
change_ref(r);
с самой ссылкой ничего не станет, изменится лишь значение i. Ссылки предоставляют меньше свободы в работе с адресом, и вся процедура change_ref(...) по сути может быть расписана как получение указателя на i в качестве параметра (по ссылке, по значению - без разницы, мы ведь меняем лишь значение) и изменения содержимого указателя. А вот поиграться непосредственно с адресом ссылки, как мы это проделывали в примере allocate_memory_for_ptr(...) не получится из-за ограничений на операции со ссылками.
Следовательно, ссылки, в общем-то, удобнее в работе, нежели указатели, но все различия между ними не заканчиваются на операции разыменовывания. И, как стало понятно, параметр, передаваемый в функцию, не может быть в неё "засунут" извне - он обязательно копируется. Другое дело уже в том, что иногда эта копия бывает нежелательна.
Комментариев нет:
Отправить комментарий