Disponiendo de inlines, podemos modificar las clases
Stash
y Stack
para
hacerlas más eficientes.
//: C09:Stash4.h // Inline functions #ifndef STASH4_H #define STASH4_H #include "../require.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 sz) : size(sz), quantity(0), next(0), storage(0) {} Stash(int sz, int initQuantity) : size(sz), quantity(0), next(0), storage(0) { inflate(initQuantity); } Stash::~Stash() { if(storage != 0) delete []storage; } int add(void* element); void* fetch(int index) const { 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 count() const { return next; } }; #endif // STASH4_H ///:~
Listado 9.8. C09/Stash4.h
Obviamente las funciones pequeñas funcionan bien como inlines, pero note que las dos funciones más largas siguen siendo no-inline, dado que convertirlas a inline no representaría ninguna mejora de rendimiento.
//: C09:Stash4.cpp {O} #include "Stash4.h" #include <iostream> #include <cassert> using namespace std; const int increment = 100; 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::inflate(int increase) { assert(increase >= 0); if(increase == 0) return; 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); // Release old storage storage = b; // Point to new memory quantity = newQuantity; // Adjust the size } ///:~
Listado 9.9. C09/Stash4.cpp
Una vez más, el programa de prueba que verifica que todo funciona correctamente.
//: C09:Stash4Test.cpp //{L} Stash4 #include "Stash4.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, 100); ifstream in("Stash4Test.cpp"); assure(in, "Stash4Test.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 9.10. C09/Stash4Test.cpp
Este es el mismo programa de prueba que se usó antes, de modo que la salida debería ser básicamente la misma.
La clase Stack
incluso hace mejor uso de inline's.
//: C09:Stack4.h // With inlines #ifndef STACK4_H #define STACK4_H #include "../require.h" class Stack { struct Link { void* data; Link* next; Link(void* dat, Link* nxt): data(dat), next(nxt) {} }* head; public: Stack() : head(0) {} ~Stack() { require(head == 0, "Stack not empty"); } void push(void* dat) { head = new Link(dat, head); } void* peek() const { return head ? head->data : 0; } void* pop() { if(head == 0) return 0; void* result = head->data; Link* oldHead = head; head = head->next; delete oldHead; return result; } }; #endif // STACK4_H ///:~
Listado 9.11. C09/Stack4.h
Note que el destructor Link
, que se
presentó (vacío) en la versión anterior de
Stack
, ha sido eliminado. En
pop()
, la expresión delete
oldHead
simplemente libera la memoria usada por
Link
(no destruye el objeto
data
apuntado por el
Link
).
La mayoría de las funciones inline quedan bastante bien
obviamente, en especial para
Link
. Incluso
pop()
parece justificado, aunque
siempre que haya sentencias condicionales o variables locales no
está claro que las inlines sean beneficiosas. Aquí, la función
es lo suficientemente pequeña así que es probable que no haga
ningún daño.
Si todas sus funciones son inline, usar la librería se convierte
en algo bastante simple porque el enlazado es innecesario, como
puede ver en el ejemplo de prueba (fíjese en que no hay
Stack4.cpp
).
//: C09:Stack4Test.cpp //{T} Stack4Test.cpp #include "Stack4.h" #include "../require.h" #include <fstream> #include <iostream> #include <string> 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; 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 << *s << endl; delete s; } } ///:~
Listado 9.12. C09/Stack4Test.cpp
La gente escribe a veces clases con todas sus funciones inline, así que la clase completa está en el fichero de cabecera (verá en este libro que yo mismo lo hago). Durante el desarrollo de un programa probablemente esto es inofensivo, aunque a veces puede hacer que las compilaciones sean más lentas. Cuando el programa se estabiliza un poco, probablemente querrá volver a hacer las funciones no-inline donde sea conveniente.