Pensar en C++ (Volumen 1) - Grupo ARCO

Pensar en C++ (Volumen 1) - Grupo ARCO Pensar en C++ (Volumen 1) - Grupo ARCO

arco.esi.uclm.es
from arco.esi.uclm.es More from this publisher
13.01.2015 Views

✐ ✐ ✐ “Volumen1” — 2012/1/12 — 13:52 — page 198 — #236 ✐ Capítulo 6. Inicialización y limpieza } void* result = head->data; Link* oldHead = head; head = head->next; delete oldHead; return result; Stack::~Stack() { require(head == 0, "Stack not empty"); } ///:~ El constructor Link:Link() simplemente inicializa los punteros data y next, así que en Stack::push(), la línea: head = new Link(dat,head); no sólo aloja un nuevo enlace (usando creación dinámica de objetos con la sentencia new, vista en el capítulo 4), también inicializa los punteros para ese enlace. Puede que le asombre que el destructor de Link no haga nada - en concreto, ¿por qué no elimina el puntero data Hay dos problemas. En el capítulo 4, en el que apareció Stack, se decía que no puede eliminar un puntero void si está apuntado a un objeto (una afirmación que se demostrará en el capítulo 13). Pero además, si el destructor de Link eliminara el puntero data, pop() retornaría un puntero a un objeto borrado, que definitivamente supone un error. A veces esto se considera como una cuestión de propiedad: Link y por consiguiente Stack sólo contienen los punteros, pero no son responsables de su limpieza. Eso significa que debe tener mucho cuidado para saber quién es el responsable. Por ejemplo, si no invoca pop() y elimina todos los punteros de Stack(), no se limpiarán automáticamente por el destructor de Stack. Esto puede ser una cuestión engorrosa y llevar a fugas de memoria, de modo que saber quién es el responsable de la limpieza de un objeto puede suponer la diferencia entre un programa correcto y uno erroneo - es decir, porqué Stack- ::~Stack() imprime un mensaje de error si el objeto Stack no está vacío en el momento su destrucción. Dado que el alojamiento y limpieza de objetos Link está oculto dentro de Stack - es parte de la implementación subyacente - no verá este suceso en el programa de prueba, aunque será el responsable de eliminar los punteros que devuelva pop(): //: C06:Stack3Test.cpp //{L} Stack3 //{T} Stack3Test.cpp // Constructors/destructors #include "Stack3.h" #include "../require.h" #include #include #include using namespace std; int main(int argc, char* argv[]) { requireArgs(argc, 1); // File name is argument ifstream in(argv[1]); assure(in, argv[1]); Stack textlines; 198 ✐ ✐ ✐ ✐

✐ ✐ ✐ “Volumen1” — 2012/1/12 — 13:52 — page 199 — #237 ✐ 6.6. Inicialización de tipos agregados string line; // Read file and store lines in the stack: while(getline(in, line)) textlines.push(new string(line)); // Pop the lines from the stack and print them: string* s; while((s = (string*)textlines.pop()) != 0) { cout

✐<br />

✐<br />

✐<br />

“Volum<strong>en</strong>1” — 2012/1/12 — 13:52 — page 198 — #236<br />

✐<br />

Capítulo 6. Inicialización y limpieza<br />

}<br />

void* result = head->data;<br />

Link* oldHead = head;<br />

head = head->next;<br />

delete oldHead;<br />

return result;<br />

Stack::~Stack() {<br />

require(head == 0, "Stack not empty");<br />

} ///:~<br />

El constructor Link:Link() simplem<strong>en</strong>te inicializa los punteros data y next,<br />

así que <strong>en</strong> Stack::push(), la línea:<br />

head = new Link(dat,head);<br />

no sólo aloja un nuevo <strong>en</strong>lace (usando creación dinámica de objetos con la s<strong>en</strong>t<strong>en</strong>cia<br />

new, vista <strong>en</strong> el capítulo 4), también inicializa los punteros para ese <strong>en</strong>lace.<br />

Puede que le asombre que el destructor de Link no haga nada - <strong>en</strong> concreto, ¿por<br />

qué no elimina el puntero data Hay dos problemas. En el capítulo 4, <strong>en</strong> el que apareció<br />

Stack, se decía que no puede eliminar un puntero void si está apuntado a un<br />

objeto (una afirmación que se demostrará <strong>en</strong> el capítulo 13). Pero además, si el destructor<br />

de Link eliminara el puntero data, pop() retornaría un puntero a un objeto<br />

borrado, que definitivam<strong>en</strong>te supone un error. A veces esto se considera como una<br />

cuestión de propiedad: Link y por consigui<strong>en</strong>te Stack sólo conti<strong>en</strong><strong>en</strong> los punteros,<br />

pero no son responsables de su limpieza. Eso significa que debe t<strong>en</strong>er mucho cuidado<br />

para saber quién es el responsable. Por ejemplo, si no invoca pop() y elimina<br />

todos los punteros de Stack(), no se limpiarán automáticam<strong>en</strong>te por el destructor<br />

de Stack. Esto puede ser una cuestión <strong>en</strong>gorrosa y llevar a fugas de memoria, de<br />

modo que saber quién es el responsable de la limpieza de un objeto puede suponer<br />

la difer<strong>en</strong>cia <strong>en</strong>tre un programa correcto y uno erroneo - es decir, porqué Stack-<br />

::~Stack() imprime un m<strong>en</strong>saje de error si el objeto Stack no está vacío <strong>en</strong> el<br />

mom<strong>en</strong>to su destrucción.<br />

Dado que el alojami<strong>en</strong>to y limpieza de objetos Link está oculto d<strong>en</strong>tro de Stack<br />

- es parte de la implem<strong>en</strong>tación subyac<strong>en</strong>te - no verá este suceso <strong>en</strong> el programa de<br />

prueba, aunque será el responsable de eliminar los punteros que devuelva pop():<br />

//: C06:Stack3Test.cpp<br />

//{L} Stack3<br />

//{T} Stack3Test.cpp<br />

// Constructors/destructors<br />

#include "Stack3.h"<br />

#include "../require.h"<br />

#include <br />

#include <br />

#include <br />

using namespace std;<br />

int main(int argc, char* argv[]) {<br />

requireArgs(argc, 1); // File name is argum<strong>en</strong>t<br />

ifstream in(argv[1]);<br />

assure(in, argv[1]);<br />

Stack textlines;<br />

198<br />

✐<br />

✐<br />

✐<br />

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!