Pensar en C++ (Volumen 1) - Grupo ARCO
Pensar en C++ (Volumen 1) - Grupo ARCO Pensar en C++ (Volumen 1) - Grupo ARCO
✐ ✐ ✐ “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
- Page 185 and 186: ✐ ✐ ✐ “Volumen1” — 2012
- Page 187 and 188: ✐ ✐ ✐ “Volumen1” — 2012
- Page 189 and 190: ✐ ✐ ✐ “Volumen1” — 2012
- Page 191 and 192: ✐ ✐ ✐ “Volumen1” — 2012
- Page 193 and 194: ✐ ✐ ✐ “Volumen1” — 2012
- Page 195 and 196: ✐ ✐ ✐ “Volumen1” — 2012
- Page 197 and 198: ✐ ✐ ✐ “Volumen1” — 2012
- Page 199 and 200: ✐ ✐ ✐ “Volumen1” — 2012
- Page 201 and 202: ✐ ✐ ✐ “Volumen1” — 2012
- Page 203 and 204: ✐ ✐ ✐ “Volumen1” — 2012
- Page 205 and 206: ✐ ✐ ✐ “Volumen1” — 2012
- Page 207 and 208: ✐ ✐ ✐ “Volumen1” — 2012
- Page 209 and 210: ✐ ✐ ✐ “Volumen1” — 2012
- Page 211 and 212: ✐ ✐ ✐ “Volumen1” — 2012
- Page 213 and 214: ✐ ✐ ✐ “Volumen1” — 2012
- Page 215 and 216: ✐ ✐ ✐ “Volumen1” — 2012
- Page 217 and 218: ✐ ✐ ✐ “Volumen1” — 2012
- Page 219 and 220: ✐ ✐ ✐ “Volumen1” — 2012
- Page 221 and 222: ✐ ✐ ✐ “Volumen1” — 2012
- Page 223 and 224: ✐ ✐ ✐ “Volumen1” — 2012
- Page 225 and 226: ✐ ✐ ✐ “Volumen1” — 2012
- Page 227 and 228: ✐ ✐ ✐ “Volumen1” — 2012
- Page 229 and 230: ✐ ✐ ✐ “Volumen1” — 2012
- Page 231 and 232: ✐ ✐ ✐ “Volumen1” — 2012
- Page 233 and 234: ✐ ✐ ✐ “Volumen1” — 2012
- Page 235: ✐ ✐ ✐ “Volumen1” — 2012
- Page 239 and 240: ✐ ✐ ✐ “Volumen1” — 2012
- Page 241 and 242: ✐ ✐ ✐ “Volumen1” — 2012
- Page 243 and 244: ✐ ✐ ✐ “Volumen1” — 2012
- Page 245 and 246: ✐ ✐ ✐ “Volumen1” — 2012
- Page 247 and 248: ✐ ✐ ✐ “Volumen1” — 2012
- Page 249 and 250: ✐ ✐ ✐ “Volumen1” — 2012
- Page 251 and 252: ✐ ✐ ✐ “Volumen1” — 2012
- Page 253 and 254: ✐ ✐ ✐ “Volumen1” — 2012
- Page 255 and 256: ✐ ✐ ✐ “Volumen1” — 2012
- Page 257 and 258: ✐ ✐ ✐ “Volumen1” — 2012
- Page 259 and 260: ✐ ✐ ✐ “Volumen1” — 2012
- Page 261 and 262: ✐ ✐ ✐ “Volumen1” — 2012
- Page 263 and 264: ✐ ✐ ✐ “Volumen1” — 2012
- Page 265 and 266: ✐ ✐ ✐ “Volumen1” — 2012
- Page 267 and 268: ✐ ✐ ✐ “Volumen1” — 2012
- Page 269 and 270: ✐ ✐ ✐ “Volumen1” — 2012
- Page 271 and 272: ✐ ✐ ✐ “Volumen1” — 2012
- Page 273 and 274: ✐ ✐ ✐ “Volumen1” — 2012
- Page 275 and 276: ✐ ✐ ✐ “Volumen1” — 2012
- Page 277 and 278: ✐ ✐ ✐ “Volumen1” — 2012
- Page 279 and 280: ✐ ✐ ✐ “Volumen1” — 2012
- Page 281 and 282: ✐ ✐ ✐ “Volumen1” — 2012
- Page 283 and 284: ✐ ✐ ✐ “Volumen1” — 2012
- Page 285 and 286: ✐ ✐ ✐ “Volumen1” — 2012
✐<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 />
✐