Los ejemplos de los capítulos anteriores tienen funciones que
tienen correspondencia directa con constructores y destructores:
initialize()
y
cleanup()
. Éste es el fichero de cabecera
de Stash
, utilizando constructor y
destructor:
//: C06:Stash2.h // With constructors & destructors #ifndef STASH2_H #define STASH2_H class Stash { int size; // Size of each space int quantity; // Number of storage spaces int next; // Next empty space // Dynamically allocated array of bytes: unsigned char* storage; void inflate(int increase); public: Stash(int size); ~Stash(); int add(void* element); void* fetch(int index); int count(); }; #endif // STASH2_H ///:~
Listado 6.4. C06/Stash2.h
Las únicas definiciones de métodos que han cambiado son
initialize()
y
cleanup()
, que han sido reemplazadas con un
constructor y un destructor.
//: C06:Stash2.cpp {O} // Constructors & destructors #include "Stash2.h" #include "../require.h" #include <iostream> #include <cassert> using namespace std; const int increment = 100; Stash::Stash(int sz) { size = sz; quantity = 0; storage = 0; next = 0; } int Stash::add(void* element) { if(next >= quantity) // Enough space left? inflate(increment); // Copy element into storage, // starting at next empty space: int startBytes = next * size; unsigned char* e = (unsigned char*)element; for(int i = 0; i < size; i++) storage[startBytes + i] = e[i]; next++; return(next - 1); // Index number } void* Stash::fetch(int index) { require(0 <= index, "Stash::fetch (-)index"); if(index >= next) return 0; // To indicate the end // Produce pointer to desired element: return &(storage[index * size]); } int Stash::count() { return next; // Number of elements in CStash } void Stash::inflate(int increase) { require(increase > 0, "Stash::inflate zero or negative increase"); int newQuantity = quantity + increase; int newBytes = newQuantity * size; int oldBytes = quantity * size; unsigned char* b = new unsigned char[newBytes]; for(int i = 0; i < oldBytes; i++) b[i] = storage[i]; // Copy old to new delete [](storage); // Old storage storage = b; // Point to new memory quantity = newQuantity; } Stash::~Stash() { if(storage != 0) { cout << "freeing storage" << endl; delete []storage; } } ///:~
Listado 6.5. C06/Stash2.cpp
Puede ver que las funciones de require.h
se
usan para vigilar errores del programador, en lugar de
assert()
. La salida de un
assert()
fallido no es tan útil como las
funciones de require.h
(que se verán más
adelante en el libro).
Dado que inflate()
es privado, el único
modo en que require()
podría fallar sería si
uno de los otros miembros pasara accidentalmente un valor
incorrecto a inflate()
. Si está seguro de
que eso no puede pasar, debería considerar eliminar el
require()
, pero debería tener en mente que
hasta que la clase sea estable, siempre existe la posibilidad de
que el código nuevo añadido a la clase podría provocar errores. El
coste de require()
es bajo (y podría ser
eliminado automáticamente por el preprocesador) mientras que la
robustez del código es alta.
Fijese cómo en el siguiente programa de prueba la definición de los
objetos Stash
aparece justo antes de
necesitarse, y cómo la inicialización aparece como parte de la
definición, en la lista de argumentos del constructor.
//: C06:Stash2Test.cpp //{L} Stash2 // Constructors & destructors #include "Stash2.h" #include "../require.h" #include <fstream> #include <iostream> #include <string> using namespace std; int main() { Stash intStash(sizeof(int)); for(int i = 0; i < 100; i++) intStash.add(&i); for(int j = 0; j < intStash.count(); j++) cout << "intStash.fetch(" << j << ") = " << *(int*)intStash.fetch(j) << endl; const int bufsize = 80; Stash stringStash(sizeof(char) * bufsize); ifstream in("Stash2Test.cpp"); assure(in, " Stash2Test.cpp"); string line; while(getline(in, line)) stringStash.add((char*)line.c_str()); int k = 0; char* cp; while((cp = (char*)stringStash.fetch(k++))!=0) cout << "stringStash.fetch(" << k << ") = " << cp << endl; } ///:~
Listado 6.6. C06/Stash2Test.cpp
También observe que se han eliminado llamadas a
cleanup()
, pero los destructores se llaman
automáticamente cuando intStash
y
stringStash
salen del ámbito.
Una cosa de la que debe ser consciente en los ejemplos con
Stash
: Tengo mucho cuidado usando sólo
tipos básicos; es decir, aquellos sin destructores. Si intenta
copiar objetos dentro de Stash
,
aparecerán todo tipo de problemas y no funcionará bien. En
realidad la Librería Estándar de C++ puede hacer copias
correctas de objetos en sus contenedores, pero es un proceso
bastante sucio y complicado. En el siguiente ejemplo de
Stack
, verá que se utilizan punteros para
esquivar esta cuestión, y en un capítulo posterior
Stash
también se convertirá para que use
punteros.