если вам нужно вернуть указатель - лучше используйте возвращаемое значение
littleadv
6
Вы можете объяснить почему? Этот отзыв не очень полезен. У меня вполне законный вопрос. В настоящее время это используется в производственном коде. Я просто не совсем понимаю, почему.
Мэтью Хогган
1
Дэвид резюмирует это довольно хорошо. Сам указатель модифицируется.
Крис
2
Я прохожу этот путь, если буду вызывать в функции оператор «Новый».
NDEthos
Ответы:
143
Вы можете передать указатель по ссылке, если вам нужно изменить указатель, а не объект, на который он указывает.
Это похоже на то, почему используются двойные указатели; использование ссылки на указатель немного безопаснее, чем использование указателей.
Так похоже на указатель на указатель, за исключением того, что на один уровень косвенности для семантики передачи по ссылке меньше?
Хочу добавить, что иногда бывает нужно вернуть указатель по ссылке. Например, если у вас есть класс, содержащий данные в динамически выделяемом массиве, но вы хотите предоставить (непостоянный) доступ к этим данным клиенту. В то же время вы не хотите, чтобы клиент мог управлять памятью через указатель.
2
@ Уильям, ты можешь подробнее рассказать об этом? Звучит интересно. Означает ли это, что пользователь этого класса может получить указатель на внутренний буфер данных и читать / писать в него, но он не может, например, освободить этот буфер, используя deleteили повторно привязав этот указатель к какому-либо другому месту в памяти? Или я ошибся?
BarbaraKwarc
2
@BarbaraKwarc Конечно. Допустим, у вас есть простая реализация динамического массива. Естественно вы перегрузите []оператора. Одна из возможных (и фактически естественных) подписей для этого была бы const T &operator[](size_t index) const. Но вы тоже могли T &operator[](size_t index). Вы могли иметь и то и другое одновременно. Последний позволит вам делать такие вещи, как myArray[jj] = 42. В то же время вы не предоставляете указатель на свои данные, поэтому вызывающий не может вмешиваться в память (например, случайно удалить ее).
Ой. Я думал, вы говорите о возврате ссылки на указатель (ваша точная формулировка: «вернуть указатель по ссылке», потому что именно об этом и спрашивал OP: передача указателей по ссылкам, а не только по простым старым ссылкам) как о некоем причудливом способе предоставление доступа для чтения / записи к буферу без разрешения манипулировать самим буфером (например, освобождать его). Другое дело - вернуть простую старую ссылку, и я это знаю.
BarbaraKwarc
65
50% программистов на C ++ любят устанавливать для своих указателей значение null после удаления:
template<typename T>void moronic_delete(T*& p){delete p;
p =nullptr;}
Без ссылки вы изменили бы только локальную копию указателя, не затрагивая вызывающего.
Я променяю глупость на то, чтобы выяснить ответ: почему это называется дебильным удалением? Что мне не хватает?
Sammaron
1
@Sammaron Если вы посмотрите историю, то раньше вызывался шаблон функции paranoid_delete, но Puppy переименовал его в moronic_delete. Он, вероятно, принадлежит к другим 50%;) В любом случае, краткий ответ заключается в том, что указатели настроек на null после deleteпочти никогда не используются (потому что они должны выходить за рамки, в любом случае) и часто затрудняют обнаружение ошибок «использования после освобождения».
fredoverflow 07
@fredoverflow Я понимаю ваш ответ, но @Puppy, кажется, deleteв целом считает глупым. Щенок, я погуглил, я не понимаю, почему удаление совершенно бесполезно (может, я тоже ужасный гуглер;)). Не могли бы вы пояснить еще немного или дать ссылку?
Sammaron
@Sammaron, в C ++ теперь есть "умные указатели", которые инкапсулируют обычные указатели и автоматически вызывают "удаление", когда они выходят за пределы области видимости. Умные указатели гораздо предпочтительнее немых указателей в стиле C, потому что они полностью устраняют эту проблему.
tjwrona1992
14
Ответ Дэвида правильный, но если он все же немного абстрактен, вот два примера:
Вы можете захотеть обнулить все освобожденные указатели, чтобы раньше выявить проблемы с памятью. В стиле C вы бы сделали:
При работе со связанными списками - часто просто представлены как указатели на следующий узел:
structNode{value_t value;Node* next;};
В этом случае, когда вы вставляете в пустой список, вы обязательно должны изменить входящий указатель, потому что результат больше не является NULLуказателем. Это случай, когда вы изменяете внешний указатель из функции, чтобы в его сигнатуре была ссылка на указатель:
Мне пришлось использовать подобный код, чтобы предоставить функции для выделения памяти переданному указателю и возврата его размера, потому что моя компания "объект" для меня, использующего STL
int iSizeOfArray(int*&piArray){
piArray =newint[iNumberOfElements];...return iNumberOfElements;}
Это нехорошо, но указатель нужно передавать по ссылке (или использовать двойной указатель). В противном случае память выделяется для локальной копии указателя, если он передается по значению, что приводит к утечке памяти.
Один из примеров - когда вы пишете функцию синтаксического анализатора и передаете ей указатель источника для чтения, если функция должна подтолкнуть этот указатель вперед за последним символом, который был правильно распознан анализатором. Использование ссылки на указатель дает понять, что функция будет перемещать исходный указатель, чтобы обновить его положение.
Как правило, вы используете ссылки на указатели, если хотите передать указатель на функцию и позволить ей переместить исходный указатель в какую-либо другую позицию, а не просто перемещать ее копию, не затрагивая оригинал.
Почему голос против? Что-то не так с тем, что я написал? Не хочешь объяснить? : P
BarbaraKwarc
0
Другая ситуация, когда вам это может понадобиться, - это если у вас есть коллекция указателей stl и вы хотите изменить их с помощью алгоритма stl. Пример for_each в c ++ 98.
Ответы:
Вы можете передать указатель по ссылке, если вам нужно изменить указатель, а не объект, на который он указывает.
Это похоже на то, почему используются двойные указатели; использование ссылки на указатель немного безопаснее, чем использование указателей.
источник
delete
или повторно привязав этот указатель к какому-либо другому месту в памяти? Или я ошибся?[]
оператора. Одна из возможных (и фактически естественных) подписей для этого была быconst T &operator[](size_t index) const
. Но вы тоже моглиT &operator[](size_t index)
. Вы могли иметь и то и другое одновременно. Последний позволит вам делать такие вещи, какmyArray[jj] = 42
. В то же время вы не предоставляете указатель на свои данные, поэтому вызывающий не может вмешиваться в память (например, случайно удалить ее).50% программистов на C ++ любят устанавливать для своих указателей значение null после удаления:
Без ссылки вы изменили бы только локальную копию указателя, не затрагивая вызывающего.
источник
paranoid_delete
, но Puppy переименовал его вmoronic_delete
. Он, вероятно, принадлежит к другим 50%;) В любом случае, краткий ответ заключается в том, что указатели настроек на null послеdelete
почти никогда не используются (потому что они должны выходить за рамки, в любом случае) и часто затрудняют обнаружение ошибок «использования после освобождения».delete
в целом считает глупым. Щенок, я погуглил, я не понимаю, почему удаление совершенно бесполезно (может, я тоже ужасный гуглер;)). Не могли бы вы пояснить еще немного или дать ссылку?Ответ Дэвида правильный, но если он все же немного абстрактен, вот два примера:
Вы можете захотеть обнулить все освобожденные указатели, чтобы раньше выявить проблемы с памятью. В стиле C вы бы сделали:
В C ++ можно сделать то же самое:
При работе со связанными списками - часто просто представлены как указатели на следующий узел:
В этом случае, когда вы вставляете в пустой список, вы обязательно должны изменить входящий указатель, потому что результат больше не является
NULL
указателем. Это случай, когда вы изменяете внешний указатель из функции, чтобы в его сигнатуре была ссылка на указатель:В этом вопросе есть пример .
источник
Мне пришлось использовать подобный код, чтобы предоставить функции для выделения памяти переданному указателю и возврата его размера, потому что моя компания "объект" для меня, использующего STL
Это нехорошо, но указатель нужно передавать по ссылке (или использовать двойной указатель). В противном случае память выделяется для локальной копии указателя, если он передается по значению, что приводит к утечке памяти.
источник
Один из примеров - когда вы пишете функцию синтаксического анализатора и передаете ей указатель источника для чтения, если функция должна подтолкнуть этот указатель вперед за последним символом, который был правильно распознан анализатором. Использование ссылки на указатель дает понять, что функция будет перемещать исходный указатель, чтобы обновить его положение.
Как правило, вы используете ссылки на указатели, если хотите передать указатель на функцию и позволить ей переместить исходный указатель в какую-либо другую позицию, а не просто перемещать ее копию, не затрагивая оригинал.
источник
Другая ситуация, когда вам это может понадобиться, - это если у вас есть коллекция указателей stl и вы хотите изменить их с помощью алгоритма stl. Пример for_each в c ++ 98.
В противном случае, если вы используете подпись changeObject (Object * item), у вас есть копия указателя, а не исходная.
источник