Sob quais circunstâncias você desejaria usar código dessa natureza em c ++?
void foo(type *&in) {...}
void fii() {
type *choochoo;
...
foo(choochoo);
}
Sob quais circunstâncias você desejaria usar código dessa natureza em c ++?
void foo(type *&in) {...}
void fii() {
type *choochoo;
...
foo(choochoo);
}
Respostas:
Você gostaria de passar um ponteiro por referência se precisar modificar o ponteiro em vez do objeto para o qual o ponteiro está apontando.
Isso é semelhante ao motivo pelo qual ponteiros duplos são usados; usar uma referência a um ponteiro é ligeiramente mais seguro do que usar ponteiros.
delete
ou religar esse ponteiro para algum outro lugar na memória? Ou eu entendi errado?
[]
operador. Uma assinatura possível (e de fato natural) para isso seria const T &operator[](size_t index) const
. Mas você também poderia ter T &operator[](size_t index)
. Você poderia ter os dois ao mesmo tempo. Este último permite que você faça coisas como myArray[jj] = 42
. Ao mesmo tempo, você não está fornecendo um ponteiro para seus dados, então o chamador não pode mexer com a memória (por exemplo, excluí-la acidentalmente).
50% dos programadores C ++ gostam de definir seus ponteiros como nulos após uma exclusão:
template<typename T>
void moronic_delete(T*& p)
{
delete p;
p = nullptr;
}
Sem a referência, você apenas mudaria uma cópia local do ponteiro, não afetando o chamador.
paranoid_delete
, mas o Puppy o renomeou para moronic_delete
. Ele provavelmente pertence aos outros 50%;) De qualquer forma, a resposta curta é que os ponteiros de configuração para nulo depois delete
quase nunca são úteis (porque eles deveriam sair do escopo, de qualquer maneira) e muitas vezes dificultam a detecção de bugs de "uso após livre".
delete
é estúpido em geral. Cachorrinho, eu fiz algumas pesquisas no Google, não consigo ver porque deletar é completamente inútil (talvez eu seja um péssimo googler também;)). Você pode explicar um pouco mais ou fornecer um link?
A resposta de David está correta, mas se ainda for um pouco abstrata, aqui estão dois exemplos:
Você pode querer zerar todos os ponteiros liberados para detectar problemas de memória mais cedo. Você faria no estilo C:
void freeAndZero(void** ptr)
{
free(*ptr);
*ptr = 0;
}
void* ptr = malloc(...);
...
freeAndZero(&ptr);
Em C ++, para fazer o mesmo, você pode fazer:
template<class T> void freeAndZero(T* &ptr)
{
delete ptr;
ptr = 0;
}
int* ptr = new int;
...
freeAndZero(ptr);
Ao lidar com listas vinculadas - frequentemente representadas simplesmente como ponteiros para um próximo nó:
struct Node
{
value_t value;
Node* next;
};
Neste caso, quando você insere na lista vazia, você deve necessariamente alterar o ponteiro de entrada porque o resultado não é mais o NULL
ponteiro. Este é o caso em que você modifica um ponteiro externo de uma função, então ele teria uma referência a um ponteiro em sua assinatura:
void insert(Node* &list)
{
...
if(!list) list = new Node(...);
...
}
Há um exemplo nesta questão .
Eu tive que usar um código como este para fornecer funções para alocar memória a um ponteiro passado e retornar seu tamanho porque minha empresa "objeto" para mim usando o STL
int iSizeOfArray(int* &piArray) {
piArray = new int[iNumberOfElements];
...
return iNumberOfElements;
}
Não é legal, mas o ponteiro deve ser passado por referência (ou use ponteiro duplo). Caso contrário, a memória é alocada para uma cópia local do ponteiro se ele for passado por valor, o que resulta em um vazamento de memória.
Um exemplo é quando você escreve uma função de analisador e passa a ela um ponteiro de origem para leitura, se a função deve empurrar esse ponteiro para frente atrás do último caractere que foi corretamente reconhecido pelo analisador. Usar uma referência a um ponteiro torna claro que a função moverá o ponteiro original para atualizar sua posição.
Em geral, você usa referências a ponteiros se quiser passar um ponteiro para uma função e deixá-lo mover o ponteiro original para alguma outra posição em vez de apenas mover uma cópia dele sem afetar o original.
Outra situação em que você pode precisar disso é se você tiver uma coleção de ponteiros stl e quiser alterá-los usando o algoritmo stl. Exemplo de for_each em 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();
}
};
Caso contrário, se você usar a assinatura changeObject (Object * item), terá uma cópia do ponteiro, não o original.