Índice:
- Escopo limitado em C ++
- Examinando o problema do escopo em C ++
- Fornecer uma solução usando o heap em C ++
Vídeo: Talk #42 - Como funciona memória: de C a JavaScript (memory management) 2024
O heap é um bloco de memória amorfo que seu programa C ++ pode acessar conforme necessário. Saiba mais sobre por que existe e como usá-lo.
Assim como é possível passar um ponteiro para uma função, é possível que uma função retorne um ponteiro. Uma função que retorna o endereço de um duplo é declarada da seguinte forma:
duplo * fn (void);
No entanto, você deve ter muito cuidado ao retornar um ponteiro. Para entender os perigos, você deve saber algo sobre o escopo variável.
Escopo limitado em C ++
Escopo é o intervalo sobre o qual uma variável é definida. Considere o seguinte fragmento de código:
// a seguinte variável é acessível para // todas as funções e definida enquanto o // programa estiver sendo executado (escopo global) int intGlobal; // a seguinte variável intChild é acessível // apenas para a função e é definida apenas // enquanto C ++ está executando filho () ou a // função quais as chamadas filho () (escopo da função) void child (void) {int intChild;} // a seguinte variável intParent tem função // scope void parent (void) {int intParent = 0; criança(); int intLater = 0; intParent = intLater;} int main (int nArgs, char * pArgs []) {parent ();}
Este fragmento de programa começa com a declaração de uma variável intGlobal. Esta variável existe a partir do momento em que o programa começa a ser executado até o término. Você diz que intGlobal "tem escopo do programa. "Você também diz que a variável" entra no escopo "mesmo antes da função principal () ser chamada.
A função main () invoca imediatamente o pai (). A primeira coisa que o processador vê em pai () é a declaração do intParent. Nesse ponto, o intParent entra no escopo - isto é, o intParent está definido e está disponível para o restante da função pai ().
A segunda declaração no pai () é a chamada para filho (). Mais uma vez, a função child () declara uma variável local, desta vez intChild. O escopo da variável intChild é limitado à função child (). Tecnicamente, intParent não está definido no escopo de child () porque child () não tem acesso ao intParent; no entanto, a variável intParent continua a existir enquanto child () está sendo executado.
Quando child () sai, a variável intChild sai do escopo. Não só o intChild não é mais acessível, já não existe. (A memória ocupada por intChild é retornada ao pool geral para ser usada para outras coisas.)
Como pai () continua a ser executado, o intLater variável entra no escopo na declaração. No ponto em que parent () retorna a main (), tanto intParent como intLater estão fora do escopo.
Como o intGlobal é declarado globalmente neste exemplo, está disponível para todas as três funções e permanece disponível para a vida útil do programa.
Examinando o problema do escopo em C ++
O seguinte segmento de código compila sem erro, mas não funciona (você não odeia isso?):
double * child (void) {double dLocalVariable; return & dLocalVariable;} void parent (void) {double * pdLocal; pdLocal = child (); * pdLocal = 1. 0;}
O problema com esta função é que dLocalVariable é definido apenas dentro do escopo da função child (). Assim, no momento em que o endereço de memória de dLocalVariable é retornado de criança (), ele se refere a uma variável que não existe mais. A memória que dLocalVariable anteriormente ocupou provavelmente está sendo usada para outra coisa.
Este erro é muito comum, pois pode avançar de várias maneiras. Infelizmente, esse erro não faz com que o programa pare imediatamente. Na verdade, o programa pode funcionar bem na maioria das vezes - ou seja, o programa continua funcionando enquanto a memória anteriormente ocupada por dLocalVariable não for reutilizada imediatamente. Tais problemas intermitentes são os mais difíceis de resolver.
Fornecer uma solução usando o heap em C ++
O problema do escopo foi originado porque C ++ retomou a memória definida localmente antes que o programador estivesse pronto. O que é necessário é um bloco de memória controlado pelo programador. Ela pode alocar a memória e colocá-la de volta quando ela quer - não porque o C ++ acha que é uma boa idéia. Esse bloco de memória é chamado de pilha.
A memória Heap é alocada usando a nova palavra-chave seguida pelo tipo de objeto a alocar. O novo comando quebra um pedaço de memória da pilha grande o suficiente para manter o tipo de objeto especificado e retorna seu endereço. Por exemplo, o seguinte aloca uma variável dupla fora do heap:
double * child (void) {double * pdLocalVariable = new double; retornar pdLocalVariable;}
Esta função agora funciona corretamente. Embora a variável pdLocalVariable saia do escopo quando a função child () retorna, a memória à qual pdLocalVariable se refere não. Um local de memória retornado por novo não fica fora do escopo até que ele seja explicitamente retornado ao heap usando a exclusão de palavras-chave, especificamente projetada para esse propósito:
void parent (void) {// child () retorna o endereço de um bloco // de memória de heap duplo * pdMyDouble = child (); // armazene um valor lá * pdMyDouble = 1. 1; // … // agora, retorne a memória ao heap delete pdMyDouble; pdMyDouble = 0; // …}
Aqui o ponteiro retornado pelo filho () é usado para armazenar um valor duplo. Depois que a função é concluída com a localização da memória, ela é retornada ao heap. A função parent () define o ponteiro como 0 depois que a memória do heap foi retornada - isso não é um requisito, mas é uma ótima idéia.
Se o programador erroneamente tentar armazenar algo em * pdMyDouble após a eliminação, o programa irá falhar imediatamente com uma mensagem de erro significativa.
Você pode usar novo para alocar matrizes do heap também, mas você deve retornar uma matriz usando a palavra-chave delete []:
int * nArray = new int [10]; nArray [0] = 0; delete [] nArray;
Technically new int [10] invoca o novo [] operador, mas funciona da mesma forma que o novo.