При каких обстоятельствах вы хотели бы использовать такой код в C ++?
void foo(type *&in) {...}
void fii() {
type *choochoo;
...
foo(choochoo);
}
При каких обстоятельствах вы хотели бы использовать такой код в C ++?
void foo(type *&in) {...}
void fii() {
type *choochoo;
...
foo(choochoo);
}
Ответы:
Вы можете передать указатель по ссылке, если вам нужно изменить указатель, а не объект, на который он указывает.
Это похоже на то, почему используются двойные указатели; использование ссылки на указатель немного безопаснее, чем использование указателей.
delete
или повторно привязав этот указатель к какому-либо другому месту в памяти? Или я ошибся?
[]
оператора. Одна из возможных (и фактически естественных) подписей для этого была бы const T &operator[](size_t index) const
. Но вы тоже могли T &operator[](size_t index)
. Вы могли иметь и то и другое одновременно. Последний позволит вам делать такие вещи, как myArray[jj] = 42
. В то же время вы не предоставляете указатель на свои данные, поэтому вызывающий не может вмешиваться в память (например, случайно удалить ее).
50% программистов на C ++ любят устанавливать для своих указателей значение null после удаления:
template<typename T>
void moronic_delete(T*& p)
{
delete p;
p = nullptr;
}
Без ссылки вы изменили бы только локальную копию указателя, не затрагивая вызывающего.
paranoid_delete
, но Puppy переименовал его в moronic_delete
. Он, вероятно, принадлежит к другим 50%;) В любом случае, краткий ответ заключается в том, что указатели настроек на null после delete
почти никогда не используются (потому что они должны выходить за рамки, в любом случае) и часто затрудняют обнаружение ошибок «использования после освобождения».
delete
в целом считает глупым. Щенок, я погуглил, я не понимаю, почему удаление совершенно бесполезно (может, я тоже ужасный гуглер;)). Не могли бы вы пояснить еще немного или дать ссылку?
Ответ Дэвида правильный, но если он все же немного абстрактен, вот два примера:
Вы можете захотеть обнулить все освобожденные указатели, чтобы раньше выявить проблемы с памятью. В стиле C вы бы сделали:
void freeAndZero(void** ptr)
{
free(*ptr);
*ptr = 0;
}
void* ptr = malloc(...);
...
freeAndZero(&ptr);
В C ++ можно сделать то же самое:
template<class T> void freeAndZero(T* &ptr)
{
delete ptr;
ptr = 0;
}
int* ptr = new int;
...
freeAndZero(ptr);
При работе со связанными списками - часто просто представлены как указатели на следующий узел:
struct Node
{
value_t value;
Node* next;
};
В этом случае, когда вы вставляете в пустой список, вы обязательно должны изменить входящий указатель, потому что результат больше не является NULL
указателем. Это случай, когда вы изменяете внешний указатель из функции, чтобы в его сигнатуре была ссылка на указатель:
void insert(Node* &list)
{
...
if(!list) list = new Node(...);
...
}
В этом вопросе есть пример .
Мне пришлось использовать подобный код, чтобы предоставить функции для выделения памяти переданному указателю и возврата его размера, потому что моя компания "объект" для меня, использующего STL
int iSizeOfArray(int* &piArray) {
piArray = new int[iNumberOfElements];
...
return iNumberOfElements;
}
Это нехорошо, но указатель нужно передавать по ссылке (или использовать двойной указатель). В противном случае память выделяется для локальной копии указателя, если он передается по значению, что приводит к утечке памяти.
Один из примеров - когда вы пишете функцию синтаксического анализатора и передаете ей указатель источника для чтения, если функция должна подтолкнуть этот указатель вперед за последним символом, который был правильно распознан анализатором. Использование ссылки на указатель дает понять, что функция будет перемещать исходный указатель, чтобы обновить его положение.
Как правило, вы используете ссылки на указатели, если хотите передать указатель на функцию и позволить ей переместить исходный указатель в какую-либо другую позицию, а не просто перемещать ее копию, не затрагивая оригинал.
Другая ситуация, когда вам это может понадобиться, - это если у вас есть коллекция указателей stl и вы хотите изменить их с помощью алгоритма stl. Пример for_each в c ++ 98.
struct Storage {
typedef std::list<Object*> ObjectList;
ObjectList objects;
void change() {
typedef void (*ChangeFunctionType)(Object*&);
std::for_each<ObjectList::iterator, ChangeFunctionType>
(objects.begin(), objects.end(), &Storage::changeObject);
}
static void changeObject(Object*& item) {
delete item;
item = 0;
if (someCondition) item = new Object();
}
};
В противном случае, если вы используете подпись changeObject (Object * item), у вас есть копия указателя, а не исходная.